unions as function args

F

Felix Kater

AFAIK there is no way in C to define one single function argument so
that two ore more explicitly declared types are allowed to pass
(so, either you pass the exact type or you need casts which I explicitly
do *not* want to consider here).

With the touch of a C enhancement in mind, I wonder, though, why
this shouldn't be allowed (it is *not* at least by my C compiler
gcc):

union u{
int i;
long l;
char* pc;
};

void f(u arg){
/* ... */
}

void main(int argc,char** argv){
i=99;
f(i); /* <-- INCOMPATIBLE TYPE */
}

Yes, of course, i is an int while union u is needed. On the other hand:
Wouldn't type checking be possible since all allowed types are defined
by the union u?

Thank You
Felix
 
J

Joachim Schmitz

Felix Kater said:
AFAIK there is no way in C to define one single function argument so
that two ore more explicitly declared types are allowed to pass
(so, either you pass the exact type or you need casts which I explicitly
do *not* want to consider here).

With the touch of a C enhancement in mind, I wonder, though, why
this shouldn't be allowed (it is *not* at least by my C compiler
gcc):

union u{
int i;
long l;
char* pc;
};

void f(u arg){
void f(union u u) {
/* ... */
}

void main(int argc,char** argv){
int main (void) { /* int as that's the only standard conforming way, (void)
because you apparently don't use argc or argv */
union u u;
u.i=99;
f(i); /* <-- INCOMPATIBLE TYPE */
f(u);
return 0;
}

Yes, of course, i is an int while union u is needed. On the other hand:
Wouldn't type checking be possible since all allowed types are defined
by the union u?

Thank You
Felix

Bye, Jojo
 
G

Guest

Felix said:
AFAIK there is no way in C to define one single function argument so
that two ore more explicitly declared types are allowed to pass
(so, either you pass the exact type or you need casts which I explicitly
do *not* want to consider here).

With the touch of a C enhancement in mind, I wonder, though, why
this shouldn't be allowed (it is *not* at least by my C compiler
gcc):

union u{
int i;
long l;
char* pc;
};

void f(u arg){
/* ... */
}

void main(int argc,char** argv){

main() should return int. Even if you don't actually return anything.
i=99;
f(i); /* <-- INCOMPATIBLE TYPE */
}

Yes, of course, i is an int while union u is needed. On the other hand:
Wouldn't type checking be possible since all allowed types are defined
by the union u?

It would have been possible for C to be defined in a way that allowed
this, but it isn't so. You can use f((union u) { .i = i }); (this
isn't a cast), but it looks hideous.

However, even if you manage to call f like that, how is it going to
know what type of argument you passed?
 
F

Felix Kater

It would have been possible for C to be defined in a way that allowed
this, but it isn't so. You can use f((union u) { .i = i }); (this
isn't a cast), but it looks hideous.
Interesting.

However, even if you manage to call f like that, how is it going to
know what type of argument you passed?

That, of course, would be up to further implementation by the user.

For simple types I imagine that (like the need of passing an
arg_count or similar to variable argument lists) you need to pass the
type information. (This is *not* the same as if we'd just created two
functions since it helps abstracting like arg_count does.) Complex types
(structs) could carry its type information as the first value in
the struct which is a common tecnique.

Felix
 
R

Rg

[...]
union u{
int i;
long l;
char* pc;
};

void f(u arg){
[...]

f() parameters are also wrong. The C-style of declaring struct or
union data requires the struct or union word:

void f(union u arg) {
/* ... */
 
B

Beej Jorgensen

Felix Kater said:
With the touch of a C enhancement in mind, I wonder, though, why
this shouldn't be allowed (it is *not* at least by my C compiler
gcc):

I can't think of why it would be impossible to implement this feature in
a compiler.

This is vaguely related:

I was messing with the Unix semctl() call last night:

int semctl(int semid, int semnum, int cmd, ...);

The 4th parameter is a user-defined union semun which looks like this:

union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
};

In that case, could I call semctl() like this?

int i = 1;

semctl(semid, 0, SETVAL, i);

I think I could, but only with the scary assumption that semctl()
doesn't attempt to access i as one of the other union members, where
that other member was larger in size than i.

-Beej
 
K

Keith Thompson

Felix Kater said:
AFAIK there is no way in C to define one single function argument so
that two ore more explicitly declared types are allowed to pass
(so, either you pass the exact type or you need casts which I explicitly
do *not* want to consider here).

With the touch of a C enhancement in mind, I wonder, though, why
this shouldn't be allowed (it is *not* at least by my C compiler
gcc):

union u{
int i;
long l;
char* pc;
};

void f(u arg){

"u arg" needs to be "union u arg".
/* ... */
}

void main(int argc,char** argv){

main returns int, not void.

int i = 99; (as you mentioned in a followup)
f(i); /* <-- INCOMPATIBLE TYPE */
}

Yes, of course, i is an int while union u is needed. On the other hand:
Wouldn't type checking be possible since all allowed types are defined
by the union u?

This isn't allowed because the argument i is of type int, and the
parameter arg is of type union u. They're two distinct types, and
there's no implicit conversion between them. A "union u" isn't an
int, it merely contains an int.

Now it would have been easy enough for the language to support this,
by defining an implicit conversion between a union type and any of its
member types.

But the language already has plenty of mechanisms for doing this kind
of thing. Unions are tricky enough as it is; accessing any member
other than the one most recently stored invokes undefined behavior
(though I think there are exceptions for things like character types,
and for types whose representation is guaranteed to be the same).

The function needs to know *which* member of the union is current. A
good way to do that is to wrap the union in a structure, which has
another member that tells you which union member is active:

struct variant {
enum { i_is_active, l_is_active, pc_is_active } discriminant;
union {
int i;
long l;
char *pc;
} u;
};

Constructing a value of this type is a little tricky, especially in
C90.

Or you can use a variadic function, using the <stdarg.h> header, with
a single fixed argument specifying the type of the second argument.
This is how printf() works.

There are almost infinitely many features that C *could* support, and
almost every one of them could be useful in some context. But if they
were all added, the standard document would collapse into a
gravitational singularity. The obvious solution is to add just the
features that *I* think would be useful, but nobody else would like
the result.
 
D

David Thompson

It would have been possible for C to be defined in a way that allowed
this, but it isn't so. You can use f((union u) { .i = i }); (this
isn't a cast), but it looks hideous.
gcc _does_ allow _as an extension_ casting to a union, without using
the full (C99 standard) compound-literal syntax:
f ( (union u) i )

De gustibus whether this looks hideous or not.
However, even if you manage to call f like that, how is it going to
know what type of argument you passed?

Might be (an)other argument(s), or previous state, or shared/global
state, or any number of things. It is often better design to carry the
discriminant with the variant data, but not always.

- formerly david.thompson1 || achar(64) || worldnet.att.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,995
Messages
2,570,230
Members
46,817
Latest member
DicWeils

Latest Threads

Top