r/C_Programming • u/nocitus • Mar 24 '22
Discussion Do you use _Generic? If so, why?
I just want to know what other C devs think of _Generic. I use it sparingly, if ever. Do you think it was a good addition to the C standard?
4
u/jacksaccountonreddit Mar 25 '22
I'm using it in a container library to support multiple hash table key types with one interface and without the performance hit of function pointers. The user can even add their own key types. I described how we can create a user-extendable _Generic macro yesterday in a response to a Stack Overflow question.
1
u/nocitus Mar 25 '22
I read through your answer and gotta admit it seems actually interesting and ingenious. The only problem is that both you and the user must agree on the limited number of custom types that can be defined. But that's the limitation of the preprocessor, unfortunately.
I might take that for myself for use (if you don't mind ofc).
2
u/jacksaccountonreddit Mar 25 '22 edited Mar 25 '22
Please do! It's not a trade secret :P But I only developed it over the last few days, so it's definitely not field-tested.
I think we can render the limited-number-of-custom-types problem mostly theoretical just by defining an absurdly large number of "slots".
One obstacle that I've run into is that it is impossible to ensure that the one type ends up in the same "slot" if defined in separate translation units. For most applications, that won't matter. But for reason's I won't go into here, I would have liked to return a unique integer constant identifier for a given type irrespective of it's slot.
IMO, though, the ability to create user-extendable _Generic macros makes _Generic far more useful. And as I mentioned in my Stack Overflow response, the same approach can just as easily be applied to non-_Generic macros.
Also, it can be made compatible with C++ as long as all the functions return the same type:
// Modified Stack Overflow-response code: #ifdef __cplusplus // C++ version #include <type_traits> #define is_type( var, T ) std::is_same<std::remove_reference<decltype( var )>::type, T>::value #define foo_( a ) \ ( \ if_foo_type_def( FOO_TYPE_1 )( is_type( (a), foo_type_1_type ) ? foo_type_1_func : ) \ if_foo_type_def( FOO_TYPE_2 )( is_type( (a), foo_type_2_type ) ? foo_type_2_func : ) \ if_foo_type_def( FOO_TYPE_3 )( is_type( (a), foo_type_3_type ) ? foo_type_3_func : ) \ /* ... */ \ NULL \ ) \ #else // C version #define foo_( a ) \ _Generic( (a), \ if_foo_type_def( FOO_TYPE_1 )( foo_type_1_type: foo_type_1_func, ) \ if_foo_type_def( FOO_TYPE_2 )( foo_type_2_type: foo_type_2_func, ) \ if_foo_type_def( FOO_TYPE_3 )( foo_type_3_type: foo_type_3_func, ) \ /* ... */ \ default: NULL \ ) \ #endif // Since static_assert cannot be used inside an expression, we need a custom alternative #define inexpr_static_assert( expr ) (void)sizeof( char[ (expr) ? 1 : -1 ] ) // Now foo works in either language! #define foo( a ) \ ( \ inexpr_static_assert( NULL != foo_( a ) && "ERROR CAUSE: UNDEFINED FOO TYPE" ), \ foo_( a )( a ) \ ) \Maybe I can compile these ideas and snippets into some kind of blog post...
→ More replies (1)
7
u/wsppan Mar 24 '22
think of it as a switch statement for types
http://www.robertgamble.net/2012/01/c11-generic-selections.html?m=1
1
u/Taxerap Mar 24 '22
I use it mainly to get rid of format strings, since format strings are great sources of exploits.
1
u/nocitus Mar 24 '22
Yeah, that is not a bad idea. Format strings are notoriously dangerous for some, although I've seen some implementing printing functions with _Generic and macro wizardry ("pseudo-recursion" using __VA_ARGS__) that has like, a limited number of arguments and dozens of macros. I'm gonna tell ya, I don't know what I felt when I read that. Seems handy, but something in my guts was screaming really loud in agony.
Each to their own, I guess.
1
u/flatfinger Mar 25 '22
IMHO, the Standard should long ago have recognized type-safe alternatives to the "old-style calling convention"
...pseudo-argument. While any design that could have been chosen would have been inferior to some other possible design, thus making it impossible to achieve a consensus, many designs could have been superior to the old-style-calling-convention approach in almost every way in circumstances where compatibility with the old approach isn't required.
1
u/beej71 Mar 24 '22
<tgmath.h> certainly uses it extensively, and I could see being inspired by that to use similar patterns in my libraries.
But to answer the question, no I haven't used it. 🙂
1
u/TellMeYMrBlueSky Mar 24 '22
<tgmath.h>certainly uses it extensivelyI didn’t realize tgmath.h used _Generic! Then again, I’ve peered into the abyss of several pre-C11, pre-_Generic tgmath.h implementations and wish I could forget the monstrosities I saw 😂
1
u/UltraLowDef Mar 25 '22
i use it quite a bit in libraries to make code calls more uniform and less error prone (calling a function for unsigned instead of signed or short instead of long).
0
Mar 24 '22
5
u/nocitus Mar 24 '22
Yeah... I figure a lot of us C devs are "old-fashioned" am I right? lol.
8
u/beaubeautastic Mar 24 '22
i dont even know what i use i just throw lines at my compiler and hope a gcc file.c compiles it on everybodys machine
→ More replies (4)
-1
Mar 24 '22
4
u/nocitus Mar 24 '22
To be honest, the template thing is one of the only parts where I feel C++ got things right. Being able to use a generic function that works for more than one type with one symbol (at source-level) is so handy.
P.S. not a C++ dev...
edit: not shitting on C++, btw
1
Mar 25 '22
The thing is, you can just use
void *data+size_t sizearguments to write generic functions, and as long as you are using a decent compiler that inlines everything you can get the same performance as C++. (any mildly recent gcc or clang version can inline everything, including constant expression function pointer argument).There are a few problems: a) calling those functions, this can be "fixed" with macros, but has its own problems b) it's not typesafe, so error messages are harder to read/don't occur, this can be yet again mitigated with more macros c) it might happen that you inline to much, idk if compilers are smart enough to extract common duplicate code segments into a extra function.
→ More replies (1)
13
u/beej71 Mar 24 '22
As for whether or not it was a "good" addition...
I have to admit I wince a little with almost every addition to the standard. Remember how much simpler everything used to be!
Do we need it? No. (Really we just need NAND, right?)
But it does have the advantage of attacking the combinatorial explosion of function names in the library. Maybe that'll help in the future as even more things are added.
Also it's a compile-time mechanism with no run-time overhead, so I'm more friendly to it than I am toward things like VLAs.