problem with cast and unions

T

torri

I have the following code:

typedef union
{
Str s;
float f;
} Type;

Where Str is a structure defined before. I tried to cast a float to a
Type like that:

float f = 1.0;
Type t = ((Type)f).c;

gcc compiles that code, but suncc (on OpenSolaris), reports an error:

invalid cast expression.

I know that suncc is a lot more strict than gcc. So what is the problem
with the code above ?

thank you
 
B

Ben Bacarisse

torri said:
I have the following code:

typedef union
{
Str s;
float f;
} Type;

Where Str is a structure defined before. I tried to cast a float to a
Type like that:

float f = 1.0;
Type t = ((Type)f).c;

There is no 'c' in your example. If c was meant to be f, the intent
seems to be convert to a union and the extract the float member when
we already have f as a float. I'll assume the purpose is to
initialise t using a float.
gcc compiles that code, but suncc (on OpenSolaris), reports an
error:

gcc can be made to complain as well. It is not ISO C: you can't cast
to a union type.
invalid cast expression.

I know that suncc is a lot more strict than gcc. So what is the problem
with the code above ?

It is just not in the language; you can't cast to a union or struct type.

You can use a designated initialiser:

Type t = { .f = f };

or a compound literal:

Type t = (Type){ .f = f };

This looks pointless (the first is simpler) but you can use this form
almost anywhere:

function(1, (Type){.f = 3.14});

If you don't have these C99 features, you have to set the member
explicitly with an assignment.
 
T

torri

There is no 'c' in your example.

Silly me, I've done a copy/paste without reading. It's

float f = 1.0;
Str s = ((Type)f).str;

the purpose is to get a type of the union from another of its type
gcc can be made to complain as well. It is not ISO C: you can't cast to
a union type.

ok

what should I do to get the "Str" type from the "float" type ?

thank you
 
T

torri

gcc will diagnose it too, if you tickle it right:

gcc -W -Wall -ansi -pedantic -Wformat-nonliteral -Wcast-align
-Wpointer-arith -Wbad-function-cast -Wmissing-prototypes
-Wstrict-prototypes -Wmissing-declarations -Winline -Wundef
-Wnested-externs -Wcast-qual -Wshadow -Wconversion -Wwrite-strings
-ffloat-store -O2 -fno-builtin -g -pg -c -o foo.o foo.c foo.c: In
function `main':
foo.c:13: warning: ANSI C forbids casts to union type

ok for the warnings :) That's a good set of warnings for checking
code :)
foo.c:13: union has no member named `c'

that's an error. See my other answer answer for the correct code and
purpose

thank you !
 
B

Ben Bacarisse

torri said:
Silly me, I've done a copy/paste without reading. It's

float f = 1.0;
Str s = ((Type)f).str;

<corrected in another message to:>

Str s = ((Type)f).s;
the purpose is to get a type of the union from another of its type

Ah. Well that all depends now. Doing so is sufficiently odd that I
can't suggest a best way. It could go horribly wrong in all sorts of
ways. What is the real purpose? I.e. why are you trug to do this?
There may be a better way to get the overall end result.

You did not comment on whether you have C99 compound literals.

<snip>
 
T

torri

<corrected in another message to:>

Str s = ((Type)f).s;


Ah. Well that all depends now. Doing so is sufficiently odd that I
can't suggest a best way. It could go horribly wrong in all sorts of
ways. What is the real purpose? I.e. why are you trug to do this?
There may be a better way to get the overall end result.

Ok. First, my aim is to make a library compiling with suncc. That library
is designed to interpret limited Small programs (Small is a language).
Think of it as a compiler for Small programs. Note that I didn't write
that library. I just try to compile on OpenSolaris.

The part where there is that problem is about conversion from a C float
to a Small "cell" (actually an int).

Here are the used types:

typedef int Embryo_Cell;

typedef union
{
float f;
Embryo_Cell c;
} Embryo_Float_Cell;

/** Float to Embryo_Cell */
#define EMBRYO_FLOAT_TO_CELL(f) ((Embryo_Float_Cell) f).c

and an example of use:

static Embryo_Cell
_embryo_fp(Embryo_Program *ep __UNUSED__, Embryo_Cell *params)
{
/* params[1] = long value to convert to a float */
float f;

if (params[0] != (1 * sizeof(Embryo_Cell))) return 0;
f = (float)params[1];
return EMBRYO_FLOAT_TO_CELL(f);
}

Note that the macro EMBRYO_FLOAT_TO_CELL is used a lot of time in that
part of program.

I hope that it is clearer, now :)

Vincent
 
B

Ben Bacarisse

torri said:
Ok. First, my aim is to make a library compiling with suncc. That library
is designed to interpret limited Small programs (Small is a language).
Think of it as a compiler for Small programs. Note that I didn't write
that library. I just try to compile on OpenSolaris.

My guess would have been a language implementation.
The part where there is that problem is about conversion from a C float
to a Small "cell" (actually an int).

Here are the used types:

typedef int Embryo_Cell;

typedef union
{
float f;
Embryo_Cell c;
} Embryo_Float_Cell;

/** Float to Embryo_Cell */
#define EMBRYO_FLOAT_TO_CELL(f) ((Embryo_Float_Cell) f).c

and an example of use:

static Embryo_Cell
_embryo_fp(Embryo_Program *ep __UNUSED__, Embryo_Cell *params)
{
/* params[1] = long value to convert to a float */
float f;

if (params[0] != (1 * sizeof(Embryo_Cell))) return 0;
f = (float)params[1];
return EMBRYO_FLOAT_TO_CELL(f);
}

Note that the macro EMBRYO_FLOAT_TO_CELL is used a lot of time in that
part of program.

I hope that it is clearer, now :)

Yes, but I don't see why you would try to hide the problem in a
macro. I'd write the example as:

static Embryo_Cell
_embryo_fp(Embryo_Program *ep __UNUSED__, Embryo_Cell *params)
{
/* params[1] = long value to convert to a float */
Embryo_Float_Cell efc;

if (params[0] != (1 * sizeof(Embryo_Cell))) return 0;
efc.f = (float)params[1];
return efc.c;
}

but I'd also want to look at the (float)params[1] part (it may be fine
you don't give the Embryo_Cell type).

If you can't re-write to avoid all this messing about, you can convert
via a pointer:

#define EMBRYO_FLOAT_TO_CELL(f) (((Embryo_Float_Cell *)&(f))->c)

but there is a lot of finger-crossing going on in all these cases.
 
T

torri

Yes, but I don't see why you would try to hide the problem in a macro.

as i said, i didn' write the code. Someone else thought it was easier.
I'd write the example as:

static Embryo_Cell
_embryo_fp(Embryo_Program *ep __UNUSED__, Embryo_Cell *params) {
/* params[1] = long value to convert to a float */
Embryo_Float_Cell efc;

if (params[0] != (1 * sizeof(Embryo_Cell))) return 0; efc.f =
(float)params[1];
return efc.c;
}

good enough for me, even if i don't have a macro.
If you can't re-write to avoid all this messing about, you can convert
via a pointer:

#define EMBRYO_FLOAT_TO_CELL(f) (((Embryo_Float_Cell *)&(f))->c)

but there is a lot of finger-crossing going on in all these cases.

it's too ugly. I'll do the clean way, switching between types I want.

thank you !

Vincent
 
M

mohangupta13

Ok. First, my aim is to make a library compiling with suncc. That library
is designed to interpret limited Small programs (Small is a language).
Think of it as a compiler for Small programs. Note that I didn't write
that library. I just try to compile on OpenSolaris.

My guess would have been a language implementation.


The part where there is that problem is about conversion from a C float
to a Small "cell" (actually an int).
Here are the used types:
typedef int Embryo_Cell;
typedef union
{
float f;
Embryo_Cell c;
} Embryo_Float_Cell;
/** Float to Embryo_Cell */
#define EMBRYO_FLOAT_TO_CELL(f) ((Embryo_Float_Cell) f).c
and an example of use:
static Embryo_Cell
_embryo_fp(Embryo_Program *ep __UNUSED__, Embryo_Cell *params)
{
/* params[1] = long value to convert to a float */
float f;
if (params[0] != (1 * sizeof(Embryo_Cell))) return 0;
f = (float)params[1];
return EMBRYO_FLOAT_TO_CELL(f);
}
Note that the macro EMBRYO_FLOAT_TO_CELL is used a lot of time in that
part of program.
I hope that it is clearer, now :)

Yes, but I don't see why you would try to hide the problem in a
macro. I'd write the example as:

static Embryo_Cell
_embryo_fp(Embryo_Program *ep __UNUSED__, Embryo_Cell *params)
{
/* params[1] = long value to convert to a float */
Embryo_Float_Cell efc;

if (params[0] != (1 * sizeof(Embryo_Cell))) return 0;
efc.f = (float)params[1];
return efc.c;
}

but I'd also want to look at the (float)params[1] part (it may be fine
you don't give the Embryo_Cell type).

If you can't re-write to avoid all this messing about, you can convert
via a pointer:

#define EMBRYO_FLOAT_TO_CELL(f) (((Embryo_Float_Cell *)&(f))->c)
is it also valid if the memeber c here would have been something else
(i mean say a struct) then just being a int .(through the typedef
thing)??
can anyone please tell me how is the structure assignment implemented
i mean is the copying done byte-wise or member-wise??
 
J

jameskuyper

torri said:
Ok. First, my aim is to make a library compiling with suncc. That library
is designed to interpret limited Small programs (Small is a language).
Think of it as a compiler for Small programs. Note that I didn't write
that library. I just try to compile on OpenSolaris.

The part where there is that problem is about conversion from a C float
to a Small "cell" (actually an int).

Here are the used types:

typedef int Embryo_Cell;

typedef union
{
float f;
Embryo_Cell c;
} Embryo_Float_Cell;

/** Float to Embryo_Cell */
#define EMBRYO_FLOAT_TO_CELL(f) ((Embryo_Float_Cell) f).c


The only time you can do anything like that is when the floating point
object that you're referring to is in fact a member of such a
structure, and even then, only if it was NOT the member most recently
filled:

Embryo_Float_Cell cell;
cell.c = 12345;

In that case, the following construct will work:

Embryo_Cell cc = (* (Embryo_Float_Cell*)&cell.f).c

Now cc will contain 12345.

However, given your examples below, it's clear that this macro is
intended to be used in contexts where the coditions described above
don't apply. It's simply intended to perform type punning, and as such
cannot be made portable. Among other things, it appears to be based
upon the assumption that sizeof(int) == sizeof(float). I'd recommend
checking that assumption with an assert() statement at least once,
somewhere early in your program.

Assuming that those types have the same size and alignment, there's no
need to declare the union. The desired conversion can be performed
simply as

float f = 123.456;
Embryo_Cell c = *(Embryo_Cell*)&f;

This approach is exactly as unsafe as the original code; if the
original code doesn't make your programs seriously malfunction, the
same will probably be true of this simpler approach, too.
A safer approach would use memcpy():

memcpy(&c, &f, sizeof c);

This is safer only in the sense that it doesn't have undefined
behavior. That doesn't help much, because ''c' isn't guaranteed to
contain a valid representation of an 'int'. If it contains a trap
representation, the behavior of your program becomes undefined as soon
as your program tries to use the value of C.
 
G

Guest

| I have the following code:
|
| typedef union
| {
| Str s;
| float f;
| } Type;
|
| Where Str is a structure defined before. I tried to cast a float to a
| Type like that:
|
| float f = 1.0;
| Type t = ((Type)f).c;
|
| gcc compiles that code, but suncc (on OpenSolaris), reports an error:
|
| invalid cast expression.
|
| I know that suncc is a lot more strict than gcc. So what is the problem
| with the code above ?

Casting isn't the same thing as just interpreting the bits some other way.
Casting is part of an expression involving a value. It takes a value and
yields a value. In theory, a struct can be treated as a value since it
can be passed to, and returned from, a function. But casting is more than
re-interpreting bits. Casts change sizes, too. It's all special handling.
For example, casting from double to int doesn't interpret a double's bit
pattern as if it were an int. OTOH, union isn't intended for that, but
instead is intended for re-use of another type which means that before you
access a member you must store into THAT member (the ability to re-interpret
bit patterns is a non-standard side effect the behaviour of which is specific
to a given architecture and ABI).

What are you trying to accomplish? If it is to use space sometimes for a
float, and at other times for a special struct, then be sure to store into
members before accessing them (e.g. don't store into .f then access .s or
even the other way around). If you are trying to re-interpret bit patterns
then I can't follow this until I know the definition of Str (and even then
it isn't standard or portable).
 
B

Ben Bacarisse

mohangupta13 said:
On Aug 20, 6:19 pm, Ben Bacarisse <[email protected]> wrote:
is it also valid if the memeber c here would have been something else
(i mean say a struct) then just being a int .(through the typedef
thing)??

Valid is an odd word for this since the whole idea is non-portable and
problematic. The result will be an lvalue of your struct type (if
member c is a struct) but whether what is in it makes and sense at all
is another matter.
can anyone please tell me how is the structure assignment implemented
i mean is the copying done byte-wise or member-wise??

Neither is mandated and no conforming program can tell what happened
so you don't usually care.
 
K

Keith Thompson

Ben Bacarisse said:
Valid is an odd word for this since the whole idea is non-portable and
problematic. The result will be an lvalue of your struct type (if
member c is a struct) but whether what is in it makes and sense at all
is another matter.


Neither is mandated and no conforming program can tell what happened
so you don't usually care.

Agreed, mostly -- though if copying is done member-wise, a conforming
program could use memcmp to compare the original and the copy.
It can't prove that the copying was done byte-wise, but it might
be able to prove that it wasn't.

A *strictly* conforming can't tell the difference, because as soon as
it tries it's no longer strictly conforming.
 
B

Ben Bacarisse

Keith Thompson said:
Agreed, mostly -- though if copying is done member-wise, a conforming
program could use memcmp to compare the original and the copy.
It can't prove that the copying was done byte-wise, but it might
be able to prove that it wasn't.

Hmmm... good point. Can we be sure that what is seen by the memcmp is
what was copied? I.e. is struct padding "stable"? It probably is but
I am not 100% sure.

<snip>
 
K

Keith Thompson

Ben Bacarisse said:
Hmmm... good point. Can we be sure that what is seen by the memcmp is
what was copied? I.e. is struct padding "stable"? It probably is but
I am not 100% sure.

I believe so. Any (non-bitfield) object can be viewed as an array
of unsigned char; each such element is an object in its own right,
and retains its last stored value.
 
M

Moi

I believe so. Any (non-bitfield) object can be viewed as an array of
unsigned char; each such element is an object in its own right, and
retains its last stored value.

I can see no other possibility for

*******************/

#include <string.h>

int check_copy(void *ptr, size_t siz)
{
unsigned char temp[siz]; /* VLA's to the rescue ... */

memcpy(temp, ptr, siz);
return memcmp(temp, ptr, siz);
}

/************************
then to always return 0.
even when called with:
*************************/

#include <stdio.h>
result = check_copy (stdin, sizeof stdin);

/***********
except when FILE is an incomplete type, which it probably is not.

HTH,
AvK
 
T

Tim Rentsch

Ben Bacarisse said:
Keith Thompson said:
[topic: structure assignment]
Agreed, mostly -- though if copying is done member-wise, a conforming
program could use memcmp to compare the original and the copy.
It can't prove that the copying was done byte-wise, but it might
be able to prove that it wasn't.

Hmmm... good point. Can we be sure that what is seen by the memcmp is
what was copied? I.e. is struct padding "stable"? It probably is but
I am not 100% sure.

If the struct doesn't have any bit-field members, Yes,
by 6.2.6.1p6 (padding bytes take unspecified values, not
indeterminate values).

If the struct does have bit-field members, the confusion
between padding bytes and padding bits makes the answer
unclear, but my reading is the committee expects that
padding bits and bytes will be treated equivalently in
such cases, ie, also Yes (as I understand the committee's
expectations).
 
T

Tim Rentsch

jameskuyper said:
The only time you can do anything like that is when the floating point
object that you're referring to is in fact a member of such a
structure, and even then, only if it was NOT the member most recently
filled:

Embryo_Float_Cell cell;
cell.c = 12345;

In that case, the following construct will work:

Embryo_Cell cc = (* (Embryo_Float_Cell*)&cell.f).c

Now cc will contain 12345.

However, given your examples below, it's clear that this macro is
intended to be used in contexts where the coditions described above
don't apply. It's simply intended to perform type punning, and as such
cannot be made portable. Among other things, it appears to be based
upon the assumption that sizeof(int) == sizeof(float). I'd recommend
checking that assumption with an assert() statement at least once,
somewhere early in your program.

Assuming that those types have the same size and alignment, there's no
need to declare the union. The desired conversion can be performed
simply as

float f = 123.456;
Embryo_Cell c = *(Embryo_Cell*)&f;

This approach is exactly as unsafe as the original code; if the
original code doesn't make your programs seriously malfunction, the
same will probably be true of this simpler approach, too.
A safer approach would use memcpy():

memcpy(&c, &f, sizeof c);

This is safer only in the sense that it doesn't have undefined
behavior. That doesn't help much, because ''c' isn't guaranteed to
contain a valid representation of an 'int'. If it contains a trap
representation, the behavior of your program becomes undefined as soon
as your program tries to use the value of C.

You seem to think accessing a union member other
than the last one stored into is automatically
undefined behavior. It isn't.
 
J

James Kuyper

Tim said:
You seem to think accessing a union member other
than the last one stored into is automatically
undefined behavior. It isn't.

I don't know how you reached that conclusion. My only use of the word
'undefined' in the text you quoted was in reference to accessing a trap
representation. Accessing a union member other than the one last stored
doesn't automatically guarantee that a trap representation is present;
indeed, for some types a trap representation is impossible, either
because the standard says so, or because those types are so-defined by
the particular implementation you're using. Even if a trap
representation is possible, it's presence is not guaranteed.

Note: plain 'int' is NOT one of the types for which the standard
prohibits trap representations.

If a trap representation is possible, accessing a union member other
than the one last written to is indeed one way in which it can occur.
Type-punning and using memcpy() in the way that I did are two other ways
this can happen; they're all roughly equally likely to fail, for pretty
much the same reasons. On an implementation where any one of them are
safe, they'll probably all be safe, but the standard allows
implementations where none of them are safe.

For practical purposes, it doesn't matter whether undefined behavior is
guaranteed, or merely a possibility - in either case, the correct
response to becoming aware of that fact is to fix the code.
 

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

Similar Threads


Members online

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,813
Latest member
lawrwtwinkle111

Latest Threads

Top