Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > To coerce or not...? struct sockaddr vs struct sockaddr_in

Reply
Thread Tools

To coerce or not...? struct sockaddr vs struct sockaddr_in

 
 
James Harris
Guest
Posts: n/a
 
      10-04-2003
Having updated my Debian system it now complains that I am using
an incompatible pointer type in warnings such as "passing arg 2
of 'bind' from incompatible pointer type" from code,

struct sockaddr_in sockad1;
....
retval = bind (sock1, &sockad1, sockad1len);

I can coerce the pointer with

retval = bind (sock1, (struct sockaddr *) &sockad1, sockad1len);

but that seems to me to be bad style - doesn't it overrides the
compiler's checking? I cannot instead change the defintion to
struct sockaddr sockad1 because my code uses members of the
sockaddr_in structure. Any other resolution I can think of is
messy. Why cannot the compiler accept that a pointer to a struct
sockaddr_in IS compatible with a pointer to struct sockaddr?
Should I coerce to keep the compiler happy or is there a better
way to approch the problem?

TIA,
- James


 
Reply With Quote
 
 
 
 
Kevin Easton
Guest
Posts: n/a
 
      10-04-2003
James Harris <no.email.please> wrote:
> Having updated my Debian system it now complains that I am using
> an incompatible pointer type in warnings such as "passing arg 2
> of 'bind' from incompatible pointer type" from code,
>
> struct sockaddr_in sockad1;
> ...
> retval = bind (sock1, &sockad1, sockad1len);
>
> I can coerce the pointer with
>
> retval = bind (sock1, (struct sockaddr *) &sockad1, sockad1len);
>
> but that seems to me to be bad style - doesn't it overrides the
> compiler's checking?


Yes, it is. But the bad style is not yours; it is the sockets API
you're using that is the root cause (it should have used a pointer to
union instead).

> I cannot instead change the defintion to
> struct sockaddr sockad1 because my code uses members of the
> sockaddr_in structure. Any other resolution I can think of is
> messy. Why cannot the compiler accept that a pointer to a struct
> sockaddr_in IS compatible with a pointer to struct sockaddr?


Because C doesn't work that way. Pointers to different structs are
never assignment-compatible.

> Should I coerce to keep the compiler happy or is there a better
> way to approch the problem?


Since you can't change the signature of the function, the cast is the
best solution. If you're concerned about the possibility of the cast
hiding errors, you can reduce it to one instance of the cast in your
entire code (I'm making an educated guess as to the signature of the
bind function you're calling):

int bind_in(int sockfd, struct sockaddr_in *my_addr, socklen_t addrlen)
{
return bind(sockfd, (struct sockaddr *)my_addr, addrlen);
}

Then just call bind_in() instead of bind() whenever you're using a
sockaddr_in. The only cast to get right is the one in bind_in() - the
compiler will warn you if you try to pass anything other than struct
sockaddr_in * as the second parameter of bind_in().

- Kevin.

 
Reply With Quote
 
 
 
 
James Harris
Guest
Posts: n/a
 
      10-06-2003
Kevin, Many thanks for your help. It all makes a bit more sense
now!

"Kevin Easton" <kevin@-nospam-pcug.org.au> wrote in message
news:newscache$pxo8mh$ba2$...
<snip>

> Because C doesn't work that way. Pointers to different

structs are
> never assignment-compatible.


Does this just happen for structs? The third parameter seems to
accept any of int, long etc and perversely char, float and
double, as well as any size_X parameter. How can bind be
expected to make sense of a float???

I notice from the headers that socklen_t is defined from
__socklen_t which is declared as typedef unsigned int. Would I
be right in assuming that the compiler generates code to convert
between float and unsigned int? The code works with this as a
float. Horrible.

....I have since been looking at the generated assembler and
compilation of the line
retval = bind (sock1, (struct sockaddr *) &mysock, mysocklen);
generates the following in gcc 2.95.4. The line with a - is the
line used when mysocklen is an integer, a long, or a socklen_t
(all compile identically). The lines with a + replace that line
when s1len is declared as a float. I think the fistpll writes
the float as an integer, which goes in to EDX before finding its
way to EAX and being pushed, thus converting before making the
call. Note to self: if you want fast code be careful to use
integer if that's what your library calls expect.(!)
addl $-4,%esp
- movl -152(%ebp),%eax
+ flds -152(%ebp)
+ fnstcw -670(%ebp)
+ movw -670(%ebp),%ax
+ orw $3072,%ax
+ movw %ax,-672(%ebp)
+ fldcw -672(%ebp)
+ fistpll -680(%ebp)
+ movl -680(%ebp),%edx
+ movl -676(%ebp),%ecx
+ fldcw -670(%ebp)
+ movl %edx,%eax
pushl %eax
leal -116(%ebp),%eax
pushl %eax
movl -512(%ebp),%eax
pushl %eax
call bind
addl $16,%esp
movl %eax,%eax
movl %eax,-56(%ebp)


 
Reply With Quote
 
Chris Torek
Guest
Posts: n/a
 
      10-08-2003
>"Kevin Easton" <kevin@-nospam-pcug.org.au> wrote in message
>news:newscache$pxo8mh$ba2$. ..
>>... Pointers to different structs are never assignment-compatible.


In article <3f81e0a5$0$10967$>,
James Harris <no.email.please> wrote:
>Does this just happen for structs?


Structures and unions (and "enum"s, to some extent). The reason
that "pointer to struct X" and "pointer to struct Y" are not
compatible is that the first occurrence of "struct X" creates a
new type -- the type named "struct X" -- and then "struct Y" creates
another new type, the one named "struct Y". Since these are two
new types, they must be different types, even if the contents are
the same.

As Kevin Easton said, the socket interface is not very well designed,
and as a result you *must* cast (or take a trip through "void *")
in at least one place. (The type-safety, or lack thereof, is just
one aspect of where the original BSD socket interface goes wrong.
If I could go back in time and fix it, I would try to convince
someone to replace the bind(), connect(), and listen() calls with
a single call. But this is different problem entirely, having
nothing to do with C per se.)

>The third parameter seems to
>accept any of int, long etc and perversely char, float and
>double, as well as any size_X parameter. How can [function F] be
>expected to make sense of a float???


The actual name of "function F" is not relevant here but its
prototype, if it has one -- and apparently it does on your system
-- is. That prototype is:

typedef unsigned int some_integral_type; /* as it happens */
int F(int, struct S *, some_integral_type);

In C, any call to a function that has a prototype in scope at
the point of the call makes use of the types in the prototype.
They cause each actual parameter in the call to be passed "as
if by ordinary assignment".

If you write:

int i;
float f = 3.14;

and then write:

i = f;

this instructs the compiler to truncate the ".14" part, leaving
just 3, and assigning 3 to i -- using whatever machine code sequence
is required to do that. (If the compiler can prove to itself that
this sets i to 3, this could be as simple as "move constant_3 =>
register holding i" or some such, rather than the 11 lines of
x86 assembly I will refrain from quoting. )

Since a prototyped call is "just like" assignment, a call of the
form:

extern void zog(int);
zog(f);

must likewise "assign" 3 to zog's formal parameter:

void zog(int i) { printf("zog got %d\n", i); }

On the other hand, if you omit the prototype, or fail to declare
zog() at all, then -- in C89 only -- no diagnostic is required and
the behavior is undefined. (In C99 "failure to declare zog()"
requires a diagnostic; declaring it without a prototype still
produces undefined behavior.) In practice, C compilers treat
this in the same way as calls to prototyped-but-variadic functions,
so that:

extern void zog(); /* note lack of prototype */
zog(f);

widens the value in f (still 3.14) to "double" -- changing its
representation in memory and/or registers if needed, as is the case
on the x86 -- and this works right only if zog() tries to access
the provided "double". Grabbing an "int", when the compiler passed
a "double", remains undefined, but does one of two different things
in practice: it either gets sizeof(int) bytes out of the sizeof(double)
bytes passed, or it gets an apparently-random value out of some
unset memory (typically, a "wrong register" or "wrong stack" --
e.g., an x86 compiler might pass the "double" on the FPU stack
instead of the stack at %esp, so that the "int" zog() reads at
(%esp+K) for some constant K has nothing to do with the 3.14 in
the FPU stack).

Ultimately, this boils down to a few simple rules C programmers
can follow.

- Always use complete prototypes whenever possible. Calls
to prototyped functions act like ordinary assignments,
in terms of the values passed in.

- Beware of variadic functions, which effectively have
no prototypes beyond the fixed-argument portion. The
varying arguments are "widened", so that float becomes
double, and char and short become int. The exact rules
get a bit complicated (does "unsigned short" widen to
signed int or unsigned int? this turns out to be
implementation-dependent!), and C99 introduces a new
concept of "type rank" along with its horde of new
types (long long, and all the complex types).

- Compile with the "warn me if I forgot to prototype"
option, so that you get A, instead of B.

- Define new types with "struct". The "typedef" keyword
is misleading: it means "do not define a new type".
(It defines aliases for *existing* types. In "typedef
struct S { ... } alias;", it is the "struct" part that
defines the new type.)
--
In-Real-Life: Chris Torek, Wind River Systems (BSD engineering)
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: forget about it http://67.40.109.61/torek/index.html (for the moment)
Reading email is like searching for food in the garbage, thanks to spammers.
 
Reply With Quote
 
James Harris
Guest
Posts: n/a
 
      10-09-2003
Chris,
You've touched upon a number of issues which strike a chord but
I'll maybe come back and raise some questions on them another
time. Thanks for the help just now.
- James


"Chris Torek" <> wrote in message
news:bm18t6$1h6$...
> >"Kevin Easton" <kevin@-nospam-pcug.org.au> wrote in message
> >news:newscache$pxo8mh$ba2$. ..
> >>... Pointers to different structs are never

assignment-compatible.
>
> In article <3f81e0a5$0$10967$>,
> James Harris <no.email.please> wrote:
> >Does this just happen for structs?

>
> Structures and unions (and "enum"s, to some extent). The

reason
> that "pointer to struct X" and "pointer to struct Y" are not
> compatible is that the first occurrence of "struct X" creates

a
> new type -- the type named "struct X" -- and then "struct Y"

creates
> another new type, the one named "struct Y". Since these are

two
> new types, they must be different types, even if the contents

are
> the same.
>
> As Kevin Easton said, the socket interface is not very well

designed,
> and as a result you *must* cast (or take a trip through "void

*")
> in at least one place. (The type-safety, or lack thereof, is

just
> one aspect of where the original BSD socket interface goes

wrong.
> If I could go back in time and fix it, I would try to convince
> someone to replace the bind(), connect(), and listen() calls

with
> a single call. But this is different problem entirely, having
> nothing to do with C per se.)
>
> >The third parameter seems to
> >accept any of int, long etc and perversely char, float and
> >double, as well as any size_X parameter. How can [function F]

be
> >expected to make sense of a float???

>
> The actual name of "function F" is not relevant here but its
> prototype, if it has one -- and apparently it does on your

system
> -- is. That prototype is:
>
> typedef unsigned int some_integral_type; /* as it happens

*/
> int F(int, struct S *, some_integral_type);
>
> In C, any call to a function that has a prototype in scope at
> the point of the call makes use of the types in the prototype.
> They cause each actual parameter in the call to be passed "as
> if by ordinary assignment".
>
> If you write:
>
> int i;
> float f = 3.14;
>
> and then write:
>
> i = f;
>
> this instructs the compiler to truncate the ".14" part,

leaving
> just 3, and assigning 3 to i -- using whatever machine code

sequence
> is required to do that. (If the compiler can prove to itself

that
> this sets i to 3, this could be as simple as "move constant_3

=>
> register holding i" or some such, rather than the 11 lines of
> x86 assembly I will refrain from quoting. )
>
> Since a prototyped call is "just like" assignment, a call of

the
> form:
>
> extern void zog(int);
> zog(f);
>
> must likewise "assign" 3 to zog's formal parameter:
>
> void zog(int i) { printf("zog got %d\n", i); }
>
> On the other hand, if you omit the prototype, or fail to

declare
> zog() at all, then -- in C89 only -- no diagnostic is required

and
> the behavior is undefined. (In C99 "failure to declare zog()"
> requires a diagnostic; declaring it without a prototype still
> produces undefined behavior.) In practice, C compilers treat
> this in the same way as calls to prototyped-but-variadic

functions,
> so that:
>
> extern void zog(); /* note lack of prototype */
> zog(f);
>
> widens the value in f (still 3.14) to "double" -- changing its
> representation in memory and/or registers if needed, as is the

case
> on the x86 -- and this works right only if zog() tries to

access
> the provided "double". Grabbing an "int", when the compiler

passed
> a "double", remains undefined, but does one of two different

things
> in practice: it either gets sizeof(int) bytes out of the

sizeof(double)
> bytes passed, or it gets an apparently-random value out of

some
> unset memory (typically, a "wrong register" or "wrong

stack" --
> e.g., an x86 compiler might pass the "double" on the FPU stack
> instead of the stack at %esp, so that the "int" zog() reads at
> (%esp+K) for some constant K has nothing to do with the 3.14

in
> the FPU stack).
>
> Ultimately, this boils down to a few simple rules C

programmers
> can follow.
>
> - Always use complete prototypes whenever possible. Calls
> to prototyped functions act like ordinary assignments,
> in terms of the values passed in.
>
> - Beware of variadic functions, which effectively have
> no prototypes beyond the fixed-argument portion. The
> varying arguments are "widened", so that float becomes
> double, and char and short become int. The exact rules
> get a bit complicated (does "unsigned short" widen to
> signed int or unsigned int? this turns out to be
> implementation-dependent!), and C99 introduces a new
> concept of "type rank" along with its horde of new
> types (long long, and all the complex types).
>
> - Compile with the "warn me if I forgot to prototype"
> option, so that you get A, instead of B.
>
> - Define new types with "struct". The "typedef" keyword
> is misleading: it means "do not define a new type".
> (It defines aliases for *existing* types. In "typedef
> struct S { ... } alias;", it is the "struct" part that
> defines the new type.)
> --
> In-Real-Life: Chris Torek, Wind River Systems (BSD

engineering)
> Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277

2603
> email: forget about it http://67.40.109.61/torek/index.html

(for the moment)
> Reading email is like searching for food in the garbage,

thanks to spammers.


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
how to coerce to a string in Python? Mark Tarver Python 3 04-29-2009 12:44 AM
disappearing documentation of `coerce` Alan Isaac Python 2 07-05-2007 05:06 PM
How to coerce a list of vars into a new type? Matthew Wilson Python 8 10-02-2006 06:35 PM
sockaddr_in() timeout or asynchone call ? Asterbing Perl Misc 9 03-27-2006 09:36 AM
Problem with coerce and SWIG John Fletcher Ruby 1 07-08-2003 12:16 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57