Passing literals to union arguments

R

Richard Harter

There is probably a simple way to do what I want but I don't see
it. Any suggestions are welcome.

Suppose I have a function foo with an argument that can be any of
several types and that I want to use a union for that argument.
In a header file there is the following:

#define VAL_ALT union urt_value_alt
.....

VAL_ALT {
int is_int; /* Value is an integer */
void * is_ptr; /* Value is a pointer */
};

.....

URT_RET urtree_insert (URT_HANDLE *, unsigned char *, long, VAL_ALT,int);

In a C file I want to call urtree_insert. The relevant code is:


VAL_ALT id;
.....
dat[1] = "baaaaa";

.....
id.is_int = 2;(void) urtree_insert(root,dat[1],6,id,1);

What I would like to do is pass a literal value instead of id,
e.g., something like:

urtree_insert(root,dat[1],6,2,1);

However that won't do; the compiler rightly complains about a
type mismatch. Is there a cast that one can use? If so, what
does it look like? (Assume that urtree_insert already knows what
type it should be getting.)
 
R

regis

Richard said:
#define VAL_ALT union urt_value_alt

VAL_ALT {
int is_int; /* Value is an integer */
void * is_ptr; /* Value is a pointer */
};

why not use a typedef instead of a macro ?
URT_RET urtree_insert (URT_HANDLE *, unsigned char *, long, VAL_ALT,int);
In a C file I want to call urtree_insert. The relevant code is:
VAL_ALT id;
What I would like to do is pass a literal value instead of id,

C99 allows compound literals for unions and structs.

urtree_insert (... , ... , ..., (VAL_ALT) { .is_int=6 }, ...);
 
S

santosh

Richard said:
There is probably a simple way to do what I want but I don't see
it. Any suggestions are welcome.

Suppose I have a function foo with an argument that can be any of
several types and that I want to use a union for that argument.
In a header file there is the following:

#define VAL_ALT union urt_value_alt
....

VAL_ALT {
int is_int; /* Value is an integer */
void * is_ptr; /* Value is a pointer */
};

....

URT_RET urtree_insert (URT_HANDLE *, unsigned char *, long, VAL_ALT,int);

In a C file I want to call urtree_insert. The relevant code is:


VAL_ALT id;
....
dat[1] = "baaaaa";

....
id.is_int = 2;(void) urtree_insert(root,dat[1],6,id,1);

What I would like to do is pass a literal value instead of id,
e.g., something like:

urtree_insert(root,dat[1],6,2,1);

[ ... ]

You cannot cast a value to an union type in Standard C. But what is your
problem that forces you to attempt to pass a literal in the place of an
union?

The problem is you're trying to convert a scalar value to a composite type
object. Your best strategy is probably to not to do that.
 
R

Richard Harter

why not use a typedef instead of a macro ?

Personal style - I never use typdedefs.
C99 allows compound literals for unions and structs.

urtree_insert (... , ... , ..., (VAL_ALT) { .is_int=6 }, ...);

Thanks for the suggestion. For portability reasons I would prefer to have
a C89 alterative.
 
C

Chris Torek

There is probably a simple way to do what I want but I don't see
it.

There is in C99. In C89, not so much. :)

[a function has an argument of type VAL_ALT, which is a union, eg,
with some snippage]
#define VAL_ALT union urt_value_alt
VAL_ALT {
int is_int; /* Value is an integer */
void * is_ptr; /* Value is a pointer */
};
URT_RET urtree_insert (URT_HANDLE *, unsigned char *, long, VAL_ALT,int);
VAL_ALT id;
dat[1] = "baaaaa";
id.is_int = 2;(void) urtree_insert(root,dat[1],6,id,1);
[where] ... urtree_insert ... knows what type it should be getting ...
What I would like to do is pass a literal value instead of id,
e.g., something like:
urtree_insert(root,dat[1],6,2,1);
However that won't do; the compiler rightly complains about a
type mismatch. Is there a cast that one can use?

Not a cast, but in C99, something that looks almost exactly *like*
a cast of an initializer: a "compound literal":

urtree_insert(root, dat[1], 6, (VAL_ALT){ .is_int = 2 }, 1);

You can of course wrap the compound literal up in a "#define" or
similar.

In C89, you can call a function and hope that your compiler optimizes
well:

/* static, if desired (this may also assist in optimization) */
VAL_ALT make_val_alt_int(int arg) {
VAL_ALT ret;
ret.is_int = arg;
return ret;
}
...
urtree_insert(root, dat[1], 6, make_val_alt_int(2), 1);

This works in C89 as well, where you can add "inline" to the
function, or even use the following #define:

#define make_val_alt_int(arg) ((VAL_ALT){ .is_int = arg })

I would probably go with the function/#define method myself.
 
K

Keith Thompson

Personal style - I never use typdedefs.

Not even size_t? Why on Earth not?

Personal style issues are rarely resolved, but I personally find your
use of a macro for a type name to be obfuscated, especially your use
of the macro in the type definition. I wouldn't use either a macro or
a typedef:

union urt_value_alt {
int is_int;
void *is_ptr;
};

union urt_value_alt obj;

But if you want a one-word name for the type, that's exactly what
typedefs are for.

This reminds me of the all too common question of how to compute the
size of something without using 'sizeof'.
Thanks for the suggestion. For portability reasons I would prefer to have
a C89 alterative.

Use a function.
 
R

Richard Harter

Not even size_t? Why on Earth not?

Oh, I use typedefs that other people create, I just don't create them in my
own code. It's habit. Back in the early 80's I ran into problems porting
code using typedefs. No, I don't recall what the problems were. I dare
say that whatever they might have been they were resolved long ago,
probably by the offending company having gone out of business. Be that as
it may I settled on using macros lon ago and never felt the need to use
typedefs.

Does that satisfy your curiosity?
 
R

Richard Harter

There is probably a simple way to do what I want but I don't see
it.

There is in C99. In C89, not so much. :)

[a function has an argument of type VAL_ALT, which is a union, eg,
with some snippage]
#define VAL_ALT union urt_value_alt
VAL_ALT {
int is_int; /* Value is an integer */
void * is_ptr; /* Value is a pointer */
};
URT_RET urtree_insert (URT_HANDLE *, unsigned char *, long, VAL_ALT,int);
VAL_ALT id;
dat[1] = "baaaaa";
id.is_int = 2;(void) urtree_insert(root,dat[1],6,id,1);
[where] ... urtree_insert ... knows what type it should be getting ...
What I would like to do is pass a literal value instead of id,
e.g., something like:
urtree_insert(root,dat[1],6,2,1);
However that won't do; the compiler rightly complains about a
type mismatch. Is there a cast that one can use?

Not a cast, but in C99, something that looks almost exactly *like*
a cast of an initializer: a "compound literal":

urtree_insert(root, dat[1], 6, (VAL_ALT){ .is_int = 2 }, 1);

You can of course wrap the compound literal up in a "#define" or
similar.

In C89, you can call a function and hope that your compiler optimizes
well:

/* static, if desired (this may also assist in optimization) */
VAL_ALT make_val_alt_int(int arg) {
VAL_ALT ret;
ret.is_int = arg;
return ret;
}
...
urtree_insert(root, dat[1], 6, make_val_alt_int(2), 1);

This works in C89 as well, where you can add "inline" to the
I assume that was supposed to be C99.
function, or even use the following #define:

#define make_val_alt_int(arg) ((VAL_ALT){ .is_int = arg })

I would probably go with the function/#define method myself.

Thank you. I had hoped that there was a better answer in C89 than using a
function, but apparently there isn't one. I'm not sure about the value of
the compound literal; the terminology implies that the argument has to be a
literal. If that is the case, a function is definitely preferable.
Although I asked about literals, one also would want to do the same thing
with variables, e.g.,

int x = 2;
....
urtree_insert(...,make_val_alt_arg(x),...);

As a side note I appreciate the quality of your posts. They are a model of
politeness and thoughtfulness.
 
C

Chris Torek

I assume that was supposed to be C99.

Oops, yes, indeed.
Thank you. I had hoped that there was a better answer in C89
than using a function, but apparently there isn't one.

Not really any "better", but more or less equivalent, you can
write wrapper functions. If f() is the original function and
it takes one of two kinds of arguments for arg3, you can go
from:

void f(int, int, union U);
...
f(1, 2, make_U_from_int(3));
f(1, 2, make_U_from_double(3.141592653589793238462643));

to:

...
f_int(1, 2, 3);
f_double(1, 2, 3.141592653589793238462643);

where the "outer" functions then wrap their "ordinary arithmetic"
type into the union, and pass the union to the original function.
In other words, one simply expands the core function with wrappers.

Obviously if some function f takes one argument that can have one
of 7 types and another argument that can have one of 5 types, this
leads to the cross-product number of "expanded" functions: 35 in
this case. But it does give you the option of calling the "original"
function with a union-valued variable, to avoid the overhead of
the expanded version.
I'm not sure about the value of the compound literal; the
terminology implies that the argument has to be a literal.

Compound literals need not be constants, provided they appear
in a context in which a constant is not required. Thus, for
instance, if the following is the obvious fragment of a translation
unit:

union U { int i; double d; };
extern double somevar;
union U foo = (union U){.d = somevar };

then (because "foo" has static duration, and thus the initializer
must be a constant) a diagnostic is required and compilation can
terminate. But:

void f(void) {
union U bar = (union U){.d = somevar };
...
}

is OK, because aggregates can now be computed at run-time in C99.
Compare with, e.g.:

extern int x0, x1, x2;
void g(void) {
int *p = (int []){ x0, x1, x2 };
...
}

which creates an automatic-duration object of type "int [3]", using
a compound literal. This object is an lvalue, so you can take its
address (as here).

(The exact rules for storage duration of compound-literal-created
aggregate objects are not too complicated. In a block, you generally
get an automatic, modifiable object whose duration is that of the
block. Adding "const" gives you a non-modifiable object. If the
compound literal appears outside a function, it has static duration,
and again "const" makes it non-modifiable. Obviously, when a
static object is created, you just get the one static object; the
C99 draft I use says that when creating an automatic object, you
again just get the one object, living in the innermost block, and
the values in the {}s are used to initialize that single object.)
 
M

Malcolm McLean

Keith Thompson said:
Not even size_t? Why on Earth not?
It looks ugly. I like ints for everything.
Of course when 64-bit systems come out there be no choice because the int
will no longer fit.
 
R

Richard Heathfield

Malcolm McLean said:
It looks ugly.

Beauty is in the eye of the beholder. I found that size_t grew on me
over the years, rather like lower case type names.
I like ints for everything.

So we have discovered.
Of course when 64-bit systems come out there be no choice because the
int will no longer fit.

64-bit systems have been out for some time. And so have 64-bit ints.
 
M

Malcolm McLean

Richard Heathfield said:
Malcolm McLean said:


Beauty is in the eye of the beholder. I found that size_t grew on me
over the years, rather like lower case type names.


So we have discovered.


64-bit systems have been out for some time. And so have 64-bit ints.
I used to work on the N64 which was marketed as a 64 bit system. In fact it
wasn't - the few 64 bit instructions were almost entirely useless and it was
just a 32 bit console. I liked the blendy rasteriser but critics hated it.
 
R

Richard

Malcolm McLean said:
I used to work on the N64 which was marketed as a 64 bit system. In
fact it wasn't - the few 64 bit instructions were almost entirely
useless and it was just a 32 bit console. I liked the blendy
rasteriser but critics hated it.

Incorrect. It was a fully 64 bit processor but hamstrung by a 32 bit
bus. It also had a 32 bit mode which was generally used since no one
needed 64 bit precision. Why chuck around 64 bits in an out of memory
when 32 will do the job as well and much more efficiently?
 
M

Malcolm McLean

Richard said:
Incorrect. It was a fully 64 bit processor but hamstrung by a 32 bit
bus. It also had a 32 bit mode which was generally used since no one
needed 64 bit precision. Why chuck around 64 bits in an out of memory
when 32 will do the job as well and much more efficiently?
That was the reason, yes. Integers are generally used to count things in the
computer's memory, so you only need 64 bits if you've got more than 2GB of
memory installed.
 
R

Richard

Malcolm McLean said:
That was the reason, yes. Integers are generally used to count things
in the computer's memory, so you only need 64 bits if you've got more
than 2GB of memory installed.

Integers are not "generally" used for any such thing IMO. They are used
to hold integer values which can be used for millions of things not
related to counting of memory or indexing into memory.

In the case of the N64 the integers or 32 bit values would be used to
represent bitmaps, colours and texture, sound values etc. 64 bits was
simply not necessary. Nothing necessarily to do with 2G of memory.
 
M

Malcolm McLean

Richard said:
Integers are not "generally" used for any such thing IMO. They are used
to hold integer values which can be used for millions of things not
related to counting of memory or indexing into memory.

In the case of the N64 the integers or 32 bit values would be used to
represent bitmaps, colours and texture, sound values etc. 64 bits was
simply not necessary. Nothing necessarily to do with 2G of memory.
It depends how you count "most integers". If you count the total number of
instnaces in memory, then you might have an compressed sound array that
accounts for a large number, and packed pixel encoding that accounts for
another huge number - whether the packed pixel is an "integer" or not you
can dispute.
When you count the labels, you'll find that

int i;

and

for(i=0;i <

are two of the most common strings in C source. Every indexing operation
involves an integer, most involve two - the index variable and the count.
 
R

Richard

Malcolm McLean said:
It depends how you count "most integers". If you count the total
number of instnaces in memory, then you might have an compressed sound
array that accounts for a large number, and packed pixel encoding that
accounts for another huge number - whether the packed pixel is an
"integer" or not you can dispute.
When you count the labels, you'll find that

int i;

and

for(i=0;i <

are two of the most common strings in C source. Every indexing
operation involves an integer, most involve two - the index variable
and the count.

No. Most include one. The index is generally the count.
 
M

Malcolm McLean

Richard said:
No. Most include one. The index is generally the count.

void setzero(double *x, int N)
{
while(N--)
x[N] = 0;
}

One integer indexing

void setzero(double *x, int N)
{
int i;

for(i=0;i<N;i++)
x = 0;
}

two integer indexing. We use two integers to calculate each array access. At
leat notionally, N is referenced for each operation. In fact the two
fucntions are identical and a clever compiler might optimise 2 away.
 
S

santosh

Malcolm said:
Richard said:
"Malcolm McLean" <[email protected]> writes:
No. Most include one. The index is generally the count.

void setzero(double *x, int N)
{
while(N--)
x[N] = 0;
}

One integer indexing

void setzero(double *x, int N)
{
int i;

for(i=0;i<N;i++)
x = 0;
}

two integer indexing. We use two integers to calculate each array access.
At leat notionally, N is referenced for each operation. In fact the two
fucntions are identical and a clever compiler might optimise 2 away.


The two functions are not identical. The first one fails to zero out the
first element of the array.
 
S

santosh

santosh said:
Malcolm said:
Richard said:
"Malcolm McLean" <[email protected]> writes:
When you count the labels, you'll find that

int i;

and

for(i=0;i <

are two of the most common strings in C source. Every indexing
operation involves an integer, most involve two - the index variable
and the count.

No. Most include one. The index is generally the count.

void setzero(double *x, int N)
{
while(N--)
x[N] = 0;
}

One integer indexing

void setzero(double *x, int N)
{
int i;

for(i=0;i<N;i++)
x = 0;
}

two integer indexing. We use two integers to calculate each array access.
At leat notionally, N is referenced for each operation. In fact the two
fucntions are identical and a clever compiler might optimise 2 away.


The two functions are not identical. The first one fails to zero out the
first element of the array.


Sorry about that. Bad day.
 

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,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top