0

Suppose we have a function like this:

void WonderfulFunction(float a)

Clearly, we can pass an int to wonderful_function and the C-compiler will promote the int to a float.

However, what about user-defined data types? Suppose we use a typedef statement to give a name/alias to a struct. Is there some way to define a promotion-rule, casting function, or constructor which will automatically convert a primitive to ADT (abstract data type)? I realize this can be done in C++, but this needs to be in C.

We want something like the following code to compile correctly:

// #include <niffty_promoter_castOperator_thing.h>

struct HappyStruct {
    int happy_int;
}; 
typedef struct HappyStruct HappyStruct;

/* prototype  */
void AnotherWonderfulFunction(HappyStruct hs)    

int main( ) {

    int a = 12345;

    AnotherWonderfulFunction(a); 

   // A caster/promoter included in the
   // header file specifies how to
   // construct a HappyStruct from an int

    return 0;
}  

void AnotherWonderfulFunction(HappyStruct hs)  {

    // do stuff;
}
Toothpick Anemone
  • 4,290
  • 2
  • 20
  • 42
  • "No this cannot be done" – Mooing Duck Aug 24 '17 at 20:04
  • 3
    Impossible, unless you wrap `WonderfulFunction` into a generic macro. – HolyBlackCat Aug 24 '17 at 20:04
  • Something like `int a = 12345; AnotherWonderfulFunction((foo(a));` See `_Generic` – chux - Reinstate Monica Aug 24 '17 at 20:05
  • *C-compiler will promote the `int` to a `float`*. Type `int` is only safely "promoted" to `float` if `int` has 16 bits and `float` has 32. Otherwise a compiler warning about possible loss of significance is generated. – Weather Vane Aug 24 '17 at 20:09
  • Might someone be willing to write an example of a macro that does the trick? Something that uses the language's built-in pre-processor directives, not a macro for which you would have to write a separate program to process. – Toothpick Anemone Aug 24 '17 at 20:10
  • 7
    `int` to `float` isn't a promotion, it is a [conversion](http://port70.net/~nsz/c/c11/n1570.html#6.3.1.4p2) – Antti Haapala -- Слава Україні Aug 24 '17 at 20:15
  • You can write `AnotherWonderfulFunction((HappyStruct){a});` pass as Compound Literals. Also You need `;` after prototype. – BLUEPIXY Aug 24 '17 at 20:15
  • This could only work if the `struct` has one member, so why the need for a `struct`? – Weather Vane Aug 24 '17 at 20:18
  • As long as the struct has only one (int) member, your specific example would probably even work if you simply cast the struct into an int (and hide the declaration of the function in some other module). But that's not proving a point and is also wouldn't be useful, and it would simply be wrong – tofro Aug 24 '17 at 20:28
  • 2
    *Is there some way to define a promotion-rule, casting function,* - Just a function? `HappyStruct int2HS(int a)`. Then call `AnotherWonderfulFunction(int2HS(a))` – Eugene Sh. Aug 24 '17 at 20:34
  • I think this can be an instance of [The XY Problem](http://xyproblem.info). Can you please explain briefly why do you need this? – Iharob Al Asimi Aug 24 '17 at 20:35
  • 1
    You keep saying "promote" when actually you mean "convert" – M.M Aug 24 '17 at 21:40
  • 1
    @tofro a struct cannot be cast to an int – M.M Aug 24 '17 at 21:51
  • @M.M In case the declaration is in some other module and hidden from the compiler, you don't even need a cast. Maybe I wasn't clear enough on that. The binary representations of a struct that contains an int and that of an int are identical. – tofro Aug 24 '17 at 23:26
  • @tofro the compiler will refuse to compile an attempt to cast an int to a struct; and what you describe is undefined behaviour – M.M Aug 24 '17 at 23:36
  • @M.M In the strict sense of the specification, it is UB - If you think about it a bit, you will realize it *will* work properly. I said above, it's *wrong*. – tofro Aug 24 '17 at 23:38

2 Answers2

3

This is "possible" with generic selection (YMMV); this described here is the closest you can get in C11. (In C99, C89 this is not possible at all). Here, AnotherWonderfulFunction(X) is a macro that will expand to (AnotherWonderfulFunction)(AsHappy(X)); the parentheses ensure that the macro is not recursively expanded.

AsHappy(X) is a macro that uses generic selection to choose one from 2 utility functions - HappyAsIs takes a struct HappyStruct as a parameter and returns it as-is, whereas HappyFromInt expects an int argument, and will return it wrapped in a struct. It needs to be done using utility functions, because at least GCC does check the language constraints for other branches, even though they're not evaluated. The original X is then passed to the selected function as an argument.

#include <stdio.h>

struct HappyStruct {
    int happy_int;
};

void AnotherWonderfulFunction(struct HappyStruct hs) {
    printf("AnotherWonderfulFunction called with hs.happy_int = %d\n", hs.happy_int);
}

struct HappyStruct HappyAsIs(struct HappyStruct s) {
    return s;
}

struct HappyStruct HappyFromInt(int val) {
    return (struct HappyStruct){ val };
}

#define AsHappy(X)                     \
    _Generic((X),                      \
        struct HappyStruct: HappyAsIs, \
        default: HappyFromInt          \
    )(X)

#define AnotherWonderfulFunction(X) (AnotherWonderfulFunction)(AsHappy(X))

int main(void) {
    int a = 42;
    float b = 65536.5;
    struct HappyStruct c = { 123 };

    AnotherWonderfulFunction(a);
    AnotherWonderfulFunction(b);
    AnotherWonderfulFunction(c);
}

and running the program produces:

% ./a.out  
AnotherWonderfulFunction called with hs.happy_int = 42
AnotherWonderfulFunction called with hs.happy_int = 65536
AnotherWonderfulFunction called with hs.happy_int = 123

However, the magic disappears as soon as you take a pointer to a function;

void (*fp)(struct HappyStruct) = AnotherWonderfulFunction;

now of course fp cannot work that way because it is not a macro.

... until you make it one ...

#define fp(X) (fp)(AsHappy(X))

All this is somewhat useless, since C11 supports compound literals:

AnotherWonderfulFunction((struct HappyStruct){ 42 });

so it is of limited use - lots of black magic to save a few keystrokes.

0

For cases where you only care about the binary representation (i.e., not in the int-to-float case), you can use GCC's __attribute__((transparent_union))

o11c
  • 15,265
  • 4
  • 50
  • 75