1

I want to initialize different union member depending if a macro expression is a constant or a variable.

So far I've found GCC extension __builtin_constant_p() which evaluates an expression and returns 1 if it is a constant and 0 otherwise. Which is exactly what I need. However I have yet to found a way how to initialize different union members based on this information.

Below is the code example:

#define TOKEN 10

typedef union 
{
    void* pv_variable;
    int literal;
} foo_t;

foo_t test_array[] =
{
    {.literal = 500},
    {.pv_variable = NULL}

    // Below doesnt work. How to make it work??
    //
    // {(TOKEN == 10) ? (.literal) : (.pv_variable) = 10},
    // {__builtin_choose_expr(__builtin_constant_p(TOKEN), .literal = 10, .pv_variable = NULL)},
    // {. ## __builtin_choose_expr(__builtin_constant_p(TOKEN), literal, pv_variable) = 10},
};

EDIT: Clarification on what I want to accomplish is detailed below.

#define INIT_UNION(val) /* TODO: */
#define SOME_VARIABLE some_variable
#define SOME_CONSTANT 10

int some_variable = 20;

typedef union 
{
    void* pv_variable;
    int literal;
} foo_t;

foo_t test_array[] =
{
    INIT_UNION(SOME_CONSTANT), /* should produce: {.literal = 10} */
    INIT_UNION(SOME_VARIABLE)  /* should produce: {.pv_variable = &some_variable} */
};
user1806687
  • 934
  • 1
  • 10
  • 27
  • Why not a good old `#if` ? – Mat Aug 09 '22 at 09:24
  • @Mat Because TOKEN is actually a macros variable argument. – user1806687 Aug 09 '22 at 09:27
  • Would adding a comma after the 2nd literal initialiser help? Me? I'd go with the KISS principle and follow @Mat... – Fe2O3 Aug 09 '22 at 09:27
  • If `#if` doesn't work then your example doesn't reflect your actual requirements (missing the 'reproducible' part of [mre]) – you should consider to modify it such that `#if` won't work here either. – Aconcagua Aug 09 '22 at 09:32
  • @Aconcagua How the hell would an #if work? I need to check if an expression is a constant or a variable AT COMPILE TIME. – user1806687 Aug 09 '22 at 09:34
  • Not really an imagination of what you *actually* try to do – just the feeling that we're talking about an [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)... – Aconcagua Aug 09 '22 at 09:41
  • @Aconcagua Clarified. – user1806687 Aug 09 '22 at 09:50
  • Hm... And how do you decide later on which union member actually is the active one? How would you correctly access the pointer if it is of type `void`? You'd need to adjust every such code at compile time as well. – Aconcagua Aug 09 '22 at 09:57
  • @Aconcagua I store the type along with the value. – user1806687 Aug 09 '22 at 09:57
  • From your comment at an answer: "`_some_variable` is not always the same. It comes from a variable argument list._" Please [edit] your question and show what you mean. A list of two variables suffices. – the busybee Aug 09 '22 at 10:17
  • C++ (since C++11) has the appropriate means to do what you want to achieve here – not even needing any platform/compiler specific stuff – see [godbolt](https://godbolt.org/z/dcGoqYrTP). Now if you moved just the initialisation to C++ and left all the rest in C (some ideas for are sketched in the link already) – could that be an option for you? – Aconcagua Aug 09 '22 at 12:19

1 Answers1

1

Your first idea was pretty close:

#define TOKEN 10

typedef union foo_u
{
    void* pv_variable;
    int literal;
} foo_t;

#define INIT_FOO_T(val) (__builtin_constant_p(val) ? (foo_t) {.literal = val} : (foo_t) {.pv_variable = val})

foo_t test_array[] =
{
    {.literal = 500},
    {.pv_variable = NULL},
    INIT_FOO_T(TOKEN)
};

Unfortunately you can use #if inside a #define, so this is the only way.

Maybe instead of:

#define INIT_FOO_T(val) (__builtin_constant_p(val) ? (foo_t) {.literal = val}

you mean:

#define INIT_FOO_T(val) (__builtin_constant_p(val) ? (foo_t) {.literal = &val}

this would make more sense but requires a different approach (it won't work anymore).

EDIT: you want the second case

#if __builtin_constant_p(TOKEN)
foo_t test_array[] =
{
    {.literal = 500},
    {.pv_variable = NULL},
#if __builtin_constant_p(TOKEN)
    {.literal = TOKEN},
#else
    {.pv_variable = &TOKEN},
#endif
};

Unfortunately I don't know how you can build a macro to do this for you.

Tenobaal
  • 637
  • 1
  • 16
  • I further clarified the question. – user1806687 Aug 09 '22 at 09:50
  • I see, this makes it a lot more complicated, because now you need a macro decision instead of just a normal compile time decision. – Tenobaal Aug 09 '22 at 09:52
  • I can make yours kind of work with: #define INIT_FOO_T(val) (__builtin_constant_p(val) ? (foo_t) {.literal = (int)val} : (foo_t) {.pv_variable = (void*)val}) And then usingINIT_FOO_T(100) for literals and INIT_FOO_T(&some_variable) for variables. – user1806687 Aug 09 '22 at 09:56
  • That would work, but it also defeats the sense of the problem. Why not use `.pv_variable = &some_variable` in the first place when you already know. – Tenobaal Aug 09 '22 at 09:58
  • some_variable is not always the same. It comes from a variable argument list. – user1806687 Aug 09 '22 at 09:59
  • This is why it would defeat the sense of the problem. I tried to point that out. I think what you are trying to do is impossible duo to how the preprocessor works. – Tenobaal Aug 09 '22 at 10:06