serialization - How to get reflection-like functionality in C, without x-macros -


related this question on software engineering serializing various struct contents on demand, found article uses x-macros create struct metadata needed "out of box" struct serialization. i've seen similar techniques "smart enums", boils down same principle, getting string representation of enum, or struct's field value name, or similar.

however experienced c programmers on stack overflow state x-macros should avoided "last resort":

i find many more related threads, unfortunately didn't bookmark them google-fu.

perhaps correct answer protocol buffers? why creating struct definition in different language (.proto definitions) , running build step generate c files preferable using built-in preprocessor same thing? , issue these techniques still don't let me retrieve single struct name, must share same definition between 2 projects , keep them in sync.

so question then: if x-macros "last resort", approach problem (easily serializing various internal data when requested different device) "first resort", or before resorting macro hell?

with bit of preprocessor magic taken boost can make macro able generate reflectable enums.

i managed construct simple proof-of-concept implementation provided below.


first, usage. following:

reflenum(myenum,     (first)     (second , 42)     (third) ) 

gets expanded to:

enum myenum {     first,     second = 42,     third, };  const char *enumtostring_myenum(enum myenum param) {     switch (param)     {       case first:         return "first";       case second:         return "second";       case third:         return "third";       default:         return "<invalid>";     } } 

thus complete program this:

#include <stdio.h>  /*  * following generated below reflenum():  *   enum myenum {first, second = 42, third};  *   const char *enumtostring_myenum(enum myenum value) {} */ reflenum(myenum,     (first)     (second , 42)     (third) )  int main() {     enum myenum foo = second;     puts(enumtostring_myenum(foo));  // -> "second"     puts(enumtostring_myenum(43));   // -> "third"     puts(enumtostring_myenum(9001)); // -> "<invalid>" } 

and here implementation itself.

it consists of 2 parts. code , preprocessor magic header shamelessly ripped off boost.

the code:

#define reflenum_impl_item(...) pputils_va_call(reflenum_impl_item_, __va_args__)(__va_args__) #define reflenum_impl_item_1(name)        name, #define reflenum_impl_item_2(name, value) name = value,  #define reflenum_impl_case(...) case pputils_va_first(__va_args__): return pputils_str(pputils_va_first(__va_args__));  #define reflenum(name, seq) \     enum name {pputils_seq_apply(seq, reflenum_impl_item)}; \     const char *enumtostring_##name(enum name param) \     { \         switch (param) \         { \             pputils_seq_apply(seq, reflenum_impl_case) \             default: return "<invalid>"; \         } \     } 

it shouldn't too hard extend code support string->enum conversion; ask in comments if you're not sure.

the magic:

note preprocessor magic has generated script, , have choose maximum enum size when generating it. generation easy , left exercise reader.

boost defaults size 64, code below generated size 4.

#define pputils_e(...) __va_args__  #define pputils_va_first(...) pputils_va_first_impl_(__va_args__,) #define pputils_va_first_impl_(x, ...) x  #define pputils_parens(...) (__va_args__) #define pputils_del_parens(...) pputils_e __va_args__  #define pputils_cc(a, b) pputils_cc_impl_(a,b) #define pputils_cc_impl_(a, b) a##b  #define pputils_call(macro, ...) macro(__va_args__)  #define pputils_va_size(...) pputils_va_size_impl_(__va_args__,4,3,2,1,0) #define pputils_va_size_impl_(i1,i2,i3,i4,size,...) size  #define pputils_str(...) pputils_str_impl_(__va_args__) #define pputils_str_impl_(...) #__va_args__  #define pputils_va_call(name, ...) pputils_cc(name, pputils_va_size(__va_args__))  #define pputils_seq_call(name, seq) pputils_cc(name, pputils_seq_size(seq))  #define pputils_seq_del_first(seq) pputils_seq_del_first_impl_ seq #define pputils_seq_del_first_impl_(...)  #define pputils_seq_first(seq) pputils_del_parens(pputils_va_first(pputils_seq_first_impl_ seq,)) #define pputils_seq_first_impl_(...) (__va_args__),  #define pputils_seq_size(seq) pputils_cc(pputils_seq_size_0 seq, _val) #define pputils_seq_size_0(...) pputils_seq_size_1 #define pputils_seq_size_1(...) pputils_seq_size_2 #define pputils_seq_size_2(...) pputils_seq_size_3 #define pputils_seq_size_3(...) pputils_seq_size_4 #define pputils_seq_size_4(...) pputils_seq_size_5 // generate pputils_seq_size_i #define pputils_seq_size_0_val 0 #define pputils_seq_size_1_val 1 #define pputils_seq_size_2_val 2 #define pputils_seq_size_3_val 3 #define pputils_seq_size_4_val 4 // generate pputils_seq_size_i_val  #define pputils_seq_apply(seq, macro) pputils_seq_call(pputils_seq_apply_, seq)(macro, seq) #define pputils_seq_apply_0(macro, seq) #define pputils_seq_apply_1(macro, seq) pputils_call(macro, pputils_seq_first(seq)) #define pputils_seq_apply_2(macro, seq) pputils_call(macro, pputils_seq_first(seq)) pputils_seq_call(pputils_seq_apply_, pputils_seq_del_first(seq))(macro, pputils_seq_del_first(seq)) #define pputils_seq_apply_3(macro, seq) pputils_call(macro, pputils_seq_first(seq)) pputils_seq_call(pputils_seq_apply_, pputils_seq_del_first(seq))(macro, pputils_seq_del_first(seq)) #define pputils_seq_apply_4(macro, seq) pputils_call(macro, pputils_seq_first(seq)) pputils_seq_call(pputils_seq_apply_, pputils_seq_del_first(seq))(macro, pputils_seq_del_first(seq)) // generate pputils_seq_apply_i 

Comments

Popular posts from this blog

PHP and MySQL WP -

android - InAppBilling registering BroadcastReceiver in AndroidManifest -

go - golang pprof for c library code -