Author here. I wish I had made it clearer that the intent of the post was "this is an interesting and surprising thing you can achieve in C" and not "this is a good idea for a real software project" or "this is a reason to use C instead of C++/Rust/Go".
Type-safe generic data structures in C
11–20 of 57 posts
Re: Type-safe generic data structures in C
#12Header only implementations of data structures are definitely the only way to accomplish type-safe generics in C, which is a great reason to avoid C! It's part of the reason why all C code bases eventually become a kind of unique, macro filled language of their own once they grow complex enough. It's not enough to know C to start working on this code, you have to know all the ins and outs of the weird macro expansion…
To add - the preprocessor is a pretty dated thing that merely uses text substitution, so you have no easy way to debug the code that the preprocessor passes to the compiler. Defines such as function-like macros throw away type safety and can have extremely unexpected behavior. For example, you might think this code will do what you want. #define SQ(x) x*x cout cout Macros are pretty much entirely unnecessary and shou…
#define SQ(x) ((x)*(x))
set off my C spidey senses. I use them even for constants in pound defines just to be consistent.That being said, there are benefits, like the type safety listed being one of them. No weird cast to void* and go to town on raw memory shenanigans you would have to do to use functions in intrusive collections in C.
Re: Type-safe generic data structures in C
#13https://github.com/Tarsnap/libcperciva/blob/master/datastruc... https://github.com/Tarsnap/libcperciva/blob/master/datastruc...
ELASTICARRAY_DECL(STR, str, char);
creates a type "STR" and the functions str_init, str_resize, str_getsize, str_append, str_shrink, str_truncate, str_get, str_iter, str_free, str_export, and str_exportdup.Re: Type-safe generic data structures in C
#14Re: Type-safe generic data structures in C
#15Earlier quoted context omitted.
To add - the preprocessor is a pretty dated thing that merely uses text substitution, so you have no easy way to debug the code that the preprocessor passes to the compiler. Defines such as function-like macros throw away type safety and can have extremely unexpected behavior. For example, you might think this code will do what you want. #define SQ(x) x*x cout cout Macros are pretty much entirely unnecessary and shou…
I agree that you shouldn't be doing this stuff in production if you don't know the ends and outs of doing this idiomatically. For instance, the lack of parenthesis ala #define SQ(x) ((x)*(x)) set off my C spidey senses. I use them even for constants in pound defines just to be consistent. That being said, there are benefits, like the type safety listed being one of them. No weird cast to void* and go to town on raw m…
#define SQ(x) ((x)*(x))
Re: Type-safe generic data structures in C
#16Header only implementations of data structures are definitely the only way to accomplish type-safe generics in C, which is a great reason to avoid C! It's part of the reason why all C code bases eventually become a kind of unique, macro filled language of their own once they grow complex enough. It's not enough to know C to start working on this code, you have to know all the ins and outs of the weird macro expansion…
I'm never again hand-writing an my_enum_to_str routine.
Re: Type-safe generic data structures in C
#17A better pattern instead of declaring via a macro that expands to a large number of functions is to define the implementation in a file, require the user to #define STACK_T and then #include the file. At the bottom of the file, #undef STACK_T. As files can be included multiple times, you can do this for each generic structure you like. A sort of poor-man’s templates, that you can reasonably step through in a debugger…
Re: Type-safe generic data structures in C
#18A better pattern instead of declaring via a macro that expands to a large number of functions is to define the implementation in a file, require the user to #define STACK_T and then #include the file. At the bottom of the file, #undef STACK_T. As files can be included multiple times, you can do this for each generic structure you like. A sort of poor-man’s templates, that you can reasonably step through in a debugger…
Note that include guards become tricky then.
Re: Type-safe generic data structures in C
#19Header only implementations of data structures are definitely the only way to accomplish type-safe generics in C, which is a great reason to avoid C! It's part of the reason why all C code bases eventually become a kind of unique, macro filled language of their own once they grow complex enough. It's not enough to know C to start working on this code, you have to know all the ins and outs of the weird macro expansion…
I've started moving to using python to generate what I need a lot of the time now in C. I'll either make a '.h' file, read it in and generate the boilerplate I need, or just do it using native python and emitting the C I need. I'm never again hand-writing an my_enum_to_str routine.
In a nutshell:
#define COLOR_LIST \
X( RED ) \
X( GREEN ) \
X( BLUE ) \
X( PURPLE )
enum Color
{
#define X(code) code,
COLOR_LIST
#undef X
};
static const char* colorNames[] =
{
#define X(code) #code,
COLOR_LIST
#undef X
};
You can even make it cleaner and store more metadata: // --- color.def
X( RED, "Red", 0xFF0000 )
X( GREEN, "Green", 0x00FF00 )
X( BLUE, "Blue", 0x0000FF )
// --- color.h
enum Color
{
#define X(code,name,mask) code,
# include "color.def"
#endif
};
static const char* colorNames[] =
{
#define X(code,name,mask) name,
# include "color.def"
#undef X
};
static unsigned int colorMasks[] =
{
#define X(code,name,mask) mask,
# include "color.def"
#undef X
};Re: Type-safe generic data structures in C
#20Earlier quoted context omitted.
I agree that you shouldn't be doing this stuff in production if you don't know the ends and outs of doing this idiomatically. For instance, the lack of parenthesis ala #define SQ(x) ((x)*(x)) set off my C spidey senses. I use them even for constants in pound defines just to be consistent. That being said, there are benefits, like the type safety listed being one of them. No weird cast to void* and go to town on raw m…
You mean: #define SQ(x) ((x)*(x))