Preprocessor macro double casting of a structure

R

riccardo

Hi folks,
I have the following structure

typedef struct{
uint16_t PADDING;
uint16_t MOST_SBits;
uint16_t MED_SBits;
uint16_t LESS_SBits;
} foo_timer;

and relative global instance:

foo_timer foo_instance;

I wrote this macro:

#define FOOMACRO() ((foo_timer)(((uint64_t)foo_instance)+200))

and used it in my main:

int main(){
return FOOMACRO().LESS_SBits;
}

Compiling gives me this error:
*error: aggregate value used where an integer was expected*

The conversion to 64bits is to allow overflow handling.

Thanks for your help
R
 
B

Barry Schwarz

Hi folks,
I have the following structure

typedef struct{
uint16_t PADDING;
uint16_t MOST_SBits;
uint16_t MED_SBits;
uint16_t LESS_SBits;
} foo_timer;

and relative global instance:

foo_timer foo_instance;

I wrote this macro:

#define FOOMACRO() ((foo_timer)(((uint64_t)foo_instance)+200))

and used it in my main:

int main(){
return FOOMACRO().LESS_SBits;
}

Compiling gives me this error:
*error: aggregate value used where an integer was expected*

The conversion to 64bits is to allow overflow handling.

Paragraph 6.5.4-2 requires both the type and the operand of the cast
operator to have a scalar type. foo_instance has type foo_timer which
is an aggregate type. The inner cast expression violates a constraint
of the language. foo_timer is still an aggregate type so the outer
cast expression violates the same constraint.

Even if the cast could work, unless foo_instance just happens to be
properly aligned for a uint64_t your code would invoke undefined
behavior.

You could get around both problems by using a union

typedef union {
uint64_t ALL_SBits;
foo_timer foo_instance;
} foo_union;
foo_union foo_aggregate;

You would then assign values to the four members of
foo_aggregate.foo_instance, do your arithmetic on
foo_aggregate.ALL_SBits, and return the value in
foo_aggregate.foo_instance.LESS_SBits. But this is a non-portable
solution.

You obviously want the treat the four 16-bit members as a single
64-bit value to force the overflow from LESS_SBits to increment
MED_SBits. This will only work if your machine is big-endian with
8-bit bytes and your structure has no padding. Consider a typical x86
little-endian machine where the four members of your structure have
the values 0, 100, 200, 65520. In hex, your structure would look like
0000 6400 c800 f0ff
Adding 200 to this 64-bit LITTLE-ENDIAN value produces
c800 6400 c800 f0ff
Thus, your four members end up with 200, 100, 200, 65520 instead of
the intended 0, 100, 201, 184.

The real solution to your problem is to compute the 64-bit value using
the << shift operator, which works on values and not representation
and is therefore insensitive to endianness

value64u = (foo_instance.MOST_SBits << 32) +
foo_instance.MED_SBits << 16) +
foo_instance.LESS_SBits;


You can then perform all the arithmetic you want on this value and
extract the desired set of bits using the >> operator, taking
advantage of the automatic modulo processing for unsigned integers.

foo_instance.MOST_SBits = value64u >> 32;
foo_instance.MED_SBits = value64u >> 16;
foo_instance.LESS_SBits = value64u;
 
B

Ben Bacarisse

riccardo said:
Hi folks,
I have the following structure

typedef struct{
uint16_t PADDING;
uint16_t MOST_SBits;
uint16_t MED_SBits;
uint16_t LESS_SBits;
} foo_timer;

and relative global instance:

foo_timer foo_instance;

I wrote this macro:

#define FOOMACRO() ((foo_timer)(((uint64_t)foo_instance)+200))

and used it in my main:

int main(){
return FOOMACRO().LESS_SBits;
}

Compiling gives me this error:
*error: aggregate value used where an integer was expected*

There's no question but I suppose it's something like "how can I do
this?". The trouble is I don't know what "this" is! Why not use a
unit64_t rather than the struct? It seems to be what you want -- a wide
integer type.

If the struct is there to "map" onto some externally-defined 48-bit
counter then you can have all sort of problems: there may be internal padding;
the fields may be in the wrong order and so on.

Narrowing things down to the issue of forcing a struct in memory to be
treated as if it had some other type (which, by the way, I strongly
advise against if there is any other way forward) you have two options:

(a) Put both the struct and the "other" types in a union.
(b) Cast a pointer and dereference that:

*(uint64_t *)&foo_instance

A macro to take that value, add 200 to it and turn it back into a struct
is so messy (and will involve either a temporary object or a C99 compound
literal) that I don't want to go there. Of the two, the union method is
clearer.

If I knew why you think you need to do this, I might be able to suggest
a better way.

<snip>
 
R

riccardo

There's no question but I suppose it's something like "how can I do
this?". The trouble is I don't know what "this" is! Why not use a
unit64_t rather than the struct? It seems to be what you want -- a wide
integer type.

If the struct is there to "map" onto some externally-defined 48-bit
counter then you can have all sort of problems: there may be internal padding;
the fields may be in the wrong order and so on.

Narrowing things down to the issue of forcing a struct in memory to be
treated as if it had some other type (which, by the way, I strongly
advise against if there is any other way forward) you have two options:

(a) Put both the struct and the "other" types in a union.
(b) Cast a pointer and dereference that:

*(uint64_t *)&foo_instance

A macro to take that value, add 200 to it and turn it back into a struct
is so messy (and will involve either a temporary object or a C99 compound
literal) that I don't want to go there. Of the two, the union method is
clearer.

If I knew why you think you need to do this, I might be able to suggest
a better way.

<snip>

All what you've said is correct (Benny Jens and Ben); the question is
exactly "how can I do this".
The reason behind is exaclty that I'm using a "almost" 16 bits
architecture with 16bits timers and using 64bits variables results in
being inefficient, as most of the times I just need to increment the
less significant bits of the counter. Nevertheless I wanted to make this
structure transparent to the higher levels of the "OS", in such a way
that I could do 64bits maths on timers.
Possibly the best solution to my needs, which doesn't require modifying
the structure into a union, is the Jens one (the one using shifts left
logical).

Thanks to *all* of you for your effort!
R
 
R

riccardo

All what you've said is correct (Benny Jens and Ben); the question is
exactly "how can I do this".
The reason behind is exaclty that I'm using a "almost" 16 bits
architecture with 16bits timers and using 64bits variables results in
being inefficient, as most of the times I just need to increment the
less significant bits of the counter. Nevertheless I wanted to make this
structure transparent to the higher levels of the "OS", in such a way
that I could do 64bits maths on timers.
Possibly the best solution to my needs, which doesn't require modifying
the structure into a union, is the Jens one (the one using shifts left
logical).

Reconsidering, Barry's shifts left logical shall be an even better one
(as it doens't require changing the value of the instance).
R
 
B

Barry Schwarz

Paragraph 6.5.4-2 requires both the type and the operand of the cast
operator to have a scalar type. foo_instance has type foo_timer which
is an aggregate type. The inner cast expression violates a constraint
of the language. foo_timer is still an aggregate type so the outer
cast expression violates the same constraint.

Even if the cast could work, unless foo_instance just happens to be
properly aligned for a uint64_t your code would invoke undefined
behavior.

Disregard this last paragraph. Casts convert values, not objects. The
alignment restriction I was thinking of applies only to the value of a
pointer after conversion.
 
D

David Thompson

Why not write that macro as

#define FOOMACRO( ) ( foo_instance.LESS_SBits += 200, foo_instance )
Loses carry sometimes (depending on old value).
or even

#define FOOMACRO( inst, val ) \
( \
inst.PADDING += ( ( uint64_t ) val >> 48 ) & 0xFFFF, \
inst.MOST_SBits += ( ( uint64_t ) val >> 32 ) & 0xFFFF, \
inst.MED_SBits += ( ( uint64_t ) val >> 16 ) & 0xFFFF, \
inst.LESS_SBits += val && 0xFFFF, \
inst )
(Obviously that last was meant to be & not && .)

Loses carry more often and sometimes more than one.
 

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

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top