16 ms·
What is your favorite C programming trick? (2009)
- ykevinator 6y agoI was excited then annoyed. I hate the SO moderators. It makes no difference to them how many people seek the answer, if they deem it off topic all those people are out of luck.
- okl 6y agoI don't get that exclusionist attitude either. If they think its subjective, why not just tag it accordingly, or move it into some sub-forum instead of outright killing the discussion.
- tester756 6y agoBecause they'd have to exclude it from search because it'd affect the results People might like it or not, but SO is a tool that provides fast access to answers on technical questions because people want to solve their problems(and learn), it's QA. If you want to discuss stuff then there are forums, reddits, discords or even HN.
- coupdejarnac 6y agoHow many times have I googled a question only to arrive at a closed SO question- too many times. It's truly annoying. Plainly people do want to discuss things on SO.
- keithnz 6y agoit's not meant to be a discussion forum, it's more a database of questions and answers ideally written to Wikipedia type quality. It gets a lot of hate for its quite strong moderation, however, it's ended up one of the best resources to find answers to programming questions, so they are doing something right.
- enriquto 6y agoActually you can regard "closed" questions as a way to find the best questions.
- smcameron 6y agoAs the original asker of the question, I agree. This question was from 12 years ago, back when Stackoverflow was a lot more fun. I asked the question because while I knew a few neat little things, I knew other people also knew cool things that I didn't know, and I wanted to find out what they might be.
- flohofwoe 6y agoDeclaring, instancing and initializing a global state struct in one go: static struct { int a; float b; const char* c; struct { float x, y, z; } nested; } state = { .a = 1, .b = 2.0f, .c = "Hello World!", .nested = { .x = 1.0f, .z = 3.0f }, }; Careful if the the struct contains big arrays though, this will let the executable size explode because a copy of the struct content is placed in the executable.
- nwellnhof 6y agoArrays of anonymous structs are also useful for non-global data: static struct { const char *key; const char *value; } elems[] = { { "k1", "v1" }, { "k2", "v2" }, { "k3", "v3" } }; for (int i=0; i<sizeof(elems)/sizeof(*elems); i++) { printf("%s => %s\n", elems[i].key, elems[i].value); }
- signa11 6y agonot a 'favorite-c-trick' but, i really find this quite cool: https://ccodearchive.net/list.html https://ccodearchive.net/list.html
- okl 6y agoUnfortunately, one can't see the code, without downloading a tarball.
- okl 6y agoHere is an assert that is static if possible: #ifndef __clang__ #define assume(expr) \ __extension__ ({ \ static_assert(__builtin_choose_expr( \ __builtin_constant_p(expr), expr, true), #expr); \ assert(__builtin_choose_expr( \ __builtin_constant_p(expr), true, expr)); \ }) #else #define assume(expr) \ __extension__ ({ \ static_assert(__builtin_constant_p(expr) ? (expr) : true, #expr); \ assert(__builtin_choose_expr( \ __builtin_constant_p(expr), true, expr)); \ }) #endif Note that the clang semantics of language extensions sometimes differ from GCC. I also like to use __auto_type very much: #define var __auto_type #define let __auto_type const As in: #define m_max(a, b) \ __extension__ ({ \ let _a = (a); \ let _b = (b); \ _a > _b ? _a : _b; \ })
- boomer_joe 6y agoWhat is the advantage of your assume macro over regular C11 static_assert / assert? Apologies if it is immediately obvious. And in case anyone else is wondering, the advantage of that m_max macro is that it evaluates each of its arguments only once.
- saagarjha 6y agoThe types of things that _Static_assert takes is substantially more limited than this construct, as it can only take an "integral constant expression" which is in practice basic integer arithmetic and nothing else. This construct works with more complicated things that are nonetheless known at compile time, such as "asdf"[4] (should be 0).
- kzrdude 6y agoI've been missing duff's device in Rust. Maybe I'm crazy.
- mtzet 6y agoInline asm?
- jcranmer 6y agoWith modern compilers (and hardware, for that matter), Duff's device is probably a deoptimization rather than an optimization. Duff's device creates an unstructured control-flow graph (a loop with multiple entry points), which is going to cause several optimizations to bail out, and will absolutely prevent any loop optimizations (such as vectorization) from kicking in for that loop. In hardware terms, for tight loops, the entire loop in regular terms is probably going to be in a hardware loop µop cache, and the loop branch predictor will probably predict the loop exit condition with 100% accuracy.
- germ 6y agoX Macros! Mostly because it's one of the more understandable and funky things you can do with the preprocessor. You can do some crazy stuff with them :) https://blog.gboards.ca/2020/02/adventures-in-obscure-c-features-2020.html https://blog.gboards.ca/2020/02/adventures-in-obscure-c-feat... https://en.m.wikipedia.org/wiki/X_Macro https://en.m.wikipedia.org/wiki/X_Macro
- kps 6y agoOh, that has a name. In C, often used with an include file instead of a body macro, and often (?) used where code wants multiple internal representations of some table of data.
- enriquto 6y agoUse VLA pointer arithmetic for invisible multiplication. Also, anything involving arrays of pointers to functions is cool by definition.
- jhallenworld 6y agoUse arrays of 1 to avoid using '.' and '&'. So if you have a structure, say: struct foo { int a, b; }; A function that works on the structure looks like this, you use '->': void pr(struct foo *n) { printf("%d %d\n", n->a, n->b); } But when you make an instance of this structure, you have to use this different syntax: struct foo p; p.a = 1; // Yuck! pr(&p); // Bad! But you can do this instead: struct foo p[1]; p->a = 1; // Very nice.. pr(p); // Yes!
- ptr 6y agoCurious, why don’t you like ‘.’ and ‘&’?
- jhallenworld 6y agoWhy does C even have two member selection operators? Anyway it means you can't cut and paste code from one place to another without changing '.' to '->' or vice-versa.
- ben-schaaf 6y ago> Why does C even have a two member selection operators? Because using `(*ptr).member` everywhere is annoying. There's plenty of times you want or need to have direct access to a member rather than always dereferencing a pointer.
- jhallenworld 6y agoptr.member should work here- I mean the compiler knows the left side is a pointer, so it should automatically dereference it.
- beagle3 6y agoC was practically a portable assembler when it was designed, and it was likely helpful for performance reasoning that all indirections were clearly visible.
- donquichotte 6y ago1. Finding the size of a struct member (lifted from the Linux kernel) #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f)) 2. Not strictly C: using `pahole` to analyze how structs are laid out in memory https://linux.die.net/man/1/pahole https://linux.die.net/man/1/pahole
- SV_BubbleTime 6y agoLast week I would have said “ok, but I’ll never need to do that”. This week I had to do exactly that (find the size of a struct member).
- jbirer 6y agoUnions for sure. You can create tagged unions with different data types and shapes (even function code to execute dynamically).
- jhallenworld 6y agoUnions are always better than casts. It would be better if casts in C looked like union deselection, because parenthesis are ugly. There really should be an infix casting operator. An example of where this matter is an AST forest, like this: struct node { int tag; }; // Generic node struct infixnode { int tag; struct node *l, *r; }; struct intnode { int tag; int val; }; But it's really ugly to use. If you have an expression represented in this AST like 'a(b(c+d))' and you want to access 'd', you need to do this: struct node *n; int d = ((struct intnode *)(((struct infixnode *)(((struct infixnode *)(((struct infixnode *)(((struct infixnode *)n)->r))->r)))))->val; But if you use unions: union node { struct infixnode infix; struct floatnode floatval; struct intnode intval; }; Then you can say this: union node *n; int val = n->infix.r->infix.r->infix.r->intval.val; The same holds for C++. In C++ you could make a class hierarchy for your AST. But you still have the casting to convert to the derived types, which is just as ugly.. But you can instead make inline access functions in the base class whose sole purpose is to do this casting, you end up something like this: node *n; int val = n->infix()->r->infix()->r->infix()->r->intval()->val;
- sys_64738 6y agostruct foo { int a; char star b; } struct foo star p = malloc(sizeof (struct foo) + 100); p->b[50] = 'x';
- enriquto 6y agoYou can quote your code (by preceeding it with four spaces) so that your stars become visible: struct foo { int a; char *b; } struct foo *p = malloc(sizeof (struct foo) + 100); p->b[50] = 'x'; ... free(p);
- wruza 6y agoIsn't it two spaces at least? Test Upd: yes it is.
- saagarjha 6y agoI think you have an initialized read in your code…
- saagarjha 6y ago* uninitialized :/
- huhtenberg 6y agoThis will crash, because 'b' will be uninitialized and pointing at some random memory spot. A conventional and working form of the same is struct foo { int a; char b[1]; } struct foo * p = malloc(sizeof (struct foo) + 99); p->b[50] = 'x'; or, if the compiler allows it, struct foo { int a; char b[0]; } struct foo * p = malloc(sizeof (struct foo) + 100); p->b[50] = 'x';
- sys_64738 6y agoRight yeah the middle with char b[1]. I was fighting with the asterisks being deleted to notice the mis-type. This method is used a lot in OS programming.
- cyanic 6y agoNamed parameters using a struct. struct calculate_args { int x, y; enum {add=0, sub} operator; }; int calculate_func(struct calculate_args args) { if (args.operator == add) return args.x + args.y; else return args.x - args.y; } #define calculate(...) calculate_func((struct calculate_args){__VA_ARGS__}) Now you can combine positional and named parameters or omit them. calculate(1, 3); // 4 calculate(8, 3, .operator=sub); // 5 calculate(.operator=sub, .y=7); // -7 Works very well in cases where there is a lot of parameters that default to 0. Keep in mind that you still need to know how structs work and you lose compile-time error detection.
- beeforpork 6y agoThis! I just wanted to post it, but you already did. :-)
- johndough 6y agoIn a similar spirit, overloading functions based on the number of parameters. Even works with tcc. #include <stdio.h> #define CONCAT2(a, b) a##b #define CONCAT(a, b) CONCAT2(a, b) #define COUNT_ARGS2(a0, a1, a2, a3, a4, a5, a6, a7, a8, N, ...) N #define COUNT_ARGS(...) COUNT_ARGS2(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1) #define foo(...) CONCAT(foo_, COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__) void foo_1(const char *a){ printf("one arg: %s\n", a); } void foo_2(int a, int b){ printf("two args: %i and %i\n", a, b); } int main(){ foo("asdf"); foo(314159, 271828); } Small gotcha: Does not work with zero parameters, but functions with zero parameters probably mess with global state and that's evil anyway.
- noncoml 6y agoIf other people use and debug your code please consider NOT using “#define” meta programming. They are a nightmare to debug.
- npsimons 6y agoIt's been a while since I've done any pure C, and I'm sure I'll be outshone by others', but I've always liked RAII in C: https://vilimpoc.org/research/raii-in-c/ https://vilimpoc.org/research/raii-in-c/
- protoman3000 6y agoA friend of mine's showed me this in a code he used for programming an educational operating system. If you have a pointer to a member of a struct, with the macro container_of you can retrieve a pointer to the enclosing struct. /* Return the offset of 'member' relative to the beginning of a struct type */ #define offsetof(type, member) ((size_t) (&((type*)0)->member)) #define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member)))
- deleted 6y ago[deleted]
- srgpqt 6y agoNo need to define offsetof, it’s part of <stddef.h> Still interesting to see the trick behind it tho.
- annilt 6y agoIt’s added to the standard because macro version has undefined behavior. (yet it still works on any compiler without a problem)
- londons_explore 6y agoI was about to ask if it was legal to dereference a null pointer and then take the address of it... I presume it is not, but I'm surprised compilers don't complain at compilation time.
- saagarjha 6y agoDereferencing a null pointer and then immediately "undoing" it by taking its address is actually legal, I believe. I think the undefined behavior here is the member access instead of the magic sequence &* which is supposed to cancel out.
- xfer 6y agoIt is used in production os like linux kernel, mostly in intrusive data structures.
- GartzenDeHaes 6y agoThe best C programming trick is to use no tricks. Making your code as boring and plain as possible greatly reduces bugs and increases productivity.
- flohofwoe 6y agoTrue, but I also consider interesting combinations of standard features, especially C99+ features useful "tricks", because C99 features which make life so much easier are usually little known in predominantly C++ circles, because C++ only supports an outdated and non-standard subset of C. E.g. this is 'named, optional arguments East Egg' is a useful trick which also improves readability: my_func((my_struct) { .bla = 1, .blub = 2, });
- pjmlp 6y agoC++ supports C features to the extent that they don't overlap with C++ ones. The compatibility with the C library is up to C11 for example.
- jcranmer 6y agoI think what GP is referring to is C++ not supporting C's designated initializers, restrict qualifiers, or flexible array members features. These are roughly the only C features not in C++ that are worth supporting (the STL in C++ works better than VLAs, and templates work better than type-generic macros). All of the other new C features are either backported C++ features with different spellings, or new functionality (mostly library) that C++ adopts.
- pjmlp 6y agoVLAs are gone, were made optional in C11, thus every compiler that jumped from C89 to C11 never bothered with them. Google even sponsored a massive effort to clean the Linux kernel of their use. A subset of designated initializers is supported in C++20.
- teddyh 6y agoThe ‘cleanup’ variable attribute¹: __attribute__((cleanup(cleanup_function))) It allows you to create, in essence, destructors for pure C code. 1. https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/Common-Variable-Attributes.html#index-cleanup-variable-attribute https://gcc.gnu.org/onlinedocs/gcc-10.2.0/gcc/Common-Variabl...
- smcameron 6y agoHa, that's my question from almost 12 years ago.
- btrask 6y agoHere's a trick that will actually help produce more secure and reliable programs. *outArg = myPtr; myPtr = NULL; free(aPtr); aPtr = NULL; Set your pointers to null when you free them! Set them to null when you transfer ownership! Stop leaving dangling pointers everywhere! Some people say they like dangling pointers because they want their program to crash if something is freed when they don't expect it to be. Good! Do this: assert(ptr); There are also many more tricks you can do once you start nulling pointers. You can use const to mark pointers that you don't own and thus can't free. You can check that buffers are all zero before you free them to catch memory leaks (this requires zeroing out other fields too of course). Please, null out your pointers and stop writing (most) use-after-free bugs!
- saagarjha 6y agoYour approach requires extra checks, though, which are easy to forget. Also, NULL is not guaranteed to be the stored as zeros, plus padding is going to make your life annoying.
- btrask 6y agoWell, dangling pointers are also easy to forget... Yes, it requires some discipline. Good code requires discipline, doesn't it? The trick of checking that buffers are zeroed is purely a debugging tool, so it's okay if it doesn't work on some platforms. And if you allocate with calloc(), the padding will be zeroed for you. It's actually very rare that you will have to call memset() with this technique.
- saagarjha 6y ago> Good code requires discipline, doesn't it? This is like the most clichéd way of saying “my code has security vulnerabilities” that there is. I have yet to see code that has remained secure solely on the “discipline” of programmers remembering to check things. > The trick of checking that buffers are zeroed is purely a debugging tool, so it's okay if it doesn't work on some platforms. Fair. > And if you allocate with calloc(), the padding will be zeroed for you. It might get unzeroed if you work with the memory.
- feisuzhu 6y agoComment toggle: foo(); //* some(); code(); which_you_want_to_comment_out(); // */ other_code(); Code between comments are 'enabled' for now, and can be commented out by simply removing a slash in the '//*'.
- sigjuice 6y agoIt is very common to use #if 0 ... #endif for this.
- cperciva 6y agoEven better, #ifdef NOTYET or similar, to help future readers (which may include "you, tomorrow") understand why it's not happening.
- SV_BubbleTime 6y agoFirst example I’ve seen in this entire post that I really like. A lot of the rest of these look like they’re just trying to be clever for the sake of it.
- a1369209993 6y agoThere's also: /** / do_thing(); /*/ do_other(); //*/ which allows quickly toggling between `thing` and `other` while debugging.
- alfl 6y agoThe "arrow decrement" is fun: int x = 10; while (x --> 0);
- a1369209993 6y agoAppropos of this, the idiomatic (!= obvious, sadly) way to iterate backwards over a array: for(size_t i=N; i-- > 0 ;) do_stuff_with(&a[i]);
- ja27 6y agoQuick-n-dirty log level: debug && printf("counter: %d\n", i);
- travisgriggs 6y agoIn general, pointer math. When I learned that myArray[10] would produce the exact same result as 10[myArray], it forced me to dig deeper into the whole C pointer model deeper and respect the architecture even more.
- lasagnaphil 6y agoStretch Buffers in the stb header-only library: https://github.com/nothings/stb/blob/master/stretchy_buffer.h https://github.com/nothings/stb/blob/master/stretchy_buffer.... This basically allows you to use std::vector<T> like vectors in C, but with an added benefit that you can subscript the vector like arr[3] rather than using unwieldly functions like vector_get(arr, 3) or vector_put(arr, 3, value).
- huhtenberg 6y agoUsing asserts AND keeping them in production builds. This really helps whipping people into shape and making them pay attention to what they write. Also helps documenting the code in a very succinct way.
- okl 6y agoAnd in terms of performance it costs next to nothing. Just don't put them into your critical loops. You can also print backtraces when an assertion fails: https://stackoverflow.com/questions/77005/how-to-automatically-generate-a-stacktrace-when-my-program-crashes https://stackoverflow.com/questions/77005/how-to-automatical...
- Someone 6y agoDepends on what’s in your asserts. If, for example, you check validity of your custom data structure in an assert, even a single assert can cost a lot to more than nothing. (Whether you should use assert for that kind of checks that probably only should run in tests is debatable, but it sometimes happens)
- parenthesis 6y agoWhat really costs nothing is putting assert(0) in any place that should be unreachable. For example: switch (...) { case A: ... case B: ... ... default: // all possible cases are dealt with above assert(0); }
- identity0 6y agoOne ugly trick for unreachable branches is this: assert(!"You gave an invalid letter"); The string literal is a pointer, so !pointer is false. And then you get a nice explanation message when the assertion fails. I wish all assert gave optional explanation messages.
- saagarjha 6y agoI often do assert(condition && "String explanation") myself.
- apankrat 6y ago#ifdef _DEBUG #define __REVIEW_ME / ## / #else #define __REVIEW_ME $review_me$ #endif When reworking a large amount of code, this can be used to tag places that may need another pass or a review. The debug version will build fine, but the release won't until all review tags are removed. This also allows adding free form comments if needed: foo += 13; __REVIEW_ME -- why 13 ?!
- sigjuice 6y agoFunction typedefs to untangle function pointers. This is from APUE, 3rd Edition. void (*signal(int signo, void (*func)(int)))(int); vs typedef void Sigfunc(int); Sigfunc *signal(int, Sigfunc *);
- Iwan-Zotow 6y agoDuff's device
- adrusi 6y agoBy far my favorite trick is the one described here https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html Effectively generators a la python or javascript, implemented using a set of macros that expand to a Duff's device control structure. #define crBegin(state) \ int *__state_ptr__ = &(state); \ *__state_ptr__ = 0; \ switch(*__state_ptr__) { case 0: #define yield(x) \ do { \ *__state_ptr__ = __LINE__; \ return x; \ case __LINE__:; \ } while (0) #define crFinish } struct counter_frame { int state; int i; }; int counter(struct counter_frame *frame) { crBegin(frame->state); frame->i = 0; while (1) { yield(frame->i++); } crFinish; } int main() { struct counter_frame my_counter; while (1) { printf("%d\n", counter(&my_counter)); } return 0; }
- dang 6y agoIf curious see also 2011 https://news.ycombinator.com/item?id=3110846 https://news.ycombinator.com/item?id=3110846
- baby 6y agoC has tricks, serious languages have coding patterns
- xd 6y agoI remember a good 20years ago a friend teaching me: !!
- saagarjha 6y ago(This "narrows" an integral value into a boolean 0/1, for those curious.)
- SV_BubbleTime 6y agoI don’t get it, and tried to look it up without success. The only way I get it is if it also reverses logic was a 0 start value becomes true. But how is this different from !x ?
- saagarjha 6y ago!!x gives you 0 if x is zero and 1 if x is anything else. !x gives you 1 if x is zero and 0 if x is anything else.
- Ne02ptzero 6y agoI've made a blog post about this some time ago[1]! Definitely not exhaustive as this thread though. [1] https://ne02ptzero.me/blog/c-tricks-that-i-use-everyday.html https://ne02ptzero.me/blog/c-tricks-that-i-use-everyday.html
- bertr4nd 6y agoZero-length arrays, used to implement a variable-sized structure with header: https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html. Although apparently it’s a GCC extension, which I didn’t realize until now.
- kevin_thibedeau 6y agoC99 made it official with "type foo[];" syntax. Better than using "type foo[1];" and potentially adding unnecessary padding.
- noncoml 6y agoLots of people reply with obscure, land-mine “#define” hacks. Am I the only one that avoids meta programming with “#define” like the plague? One might argue it’s not even C..
- zbuf 6y agoShare your own "trick" then :) one that doesn't use #define
- not2b 6y agoI like (v&(v-1))==0 (is v an exact power of 2?).
- jacknews 6y agoI rarely see the comma operator used, but it can be quite handy, eg: if (turn_on() || reset(), turn_on()) { // try to turn it on. if fail, reset and try again // it's on }
- bacon_waffle 6y agoswitch (fruit) { case APPLE: if (true) { pick_apple(); } else case BANANA: { pick_banana(); } box_fruit(); }
- matvore 6y agoMy favorite C trick is not related to programming - but to debugging and disassembly. Unlike C++, there is no name mangling, so stack traces are a breeze to read, especially with -fno-inline-functions and -O0 or -O1. There is no implicit action at all (i.e. no exception handling or destructors) so there is a simple mapping between the assembly and source code. My favorite "programming" trick only applies if I'm not sharing my code. I just forego header files entirely, and just #include the .c source files. Also, put `#pragma once` in all the .c files to avoid double-inclusion without the hassle of #if ... etc. This requires a bit more diligence since you can't have mutual recursion between source files.
- pjmlp 6y agoWriting boring clean code, without clever tricks that force everyone to keep their copy of ISO C and compiler extensions manuals open.
- scruffyherder 6y agoPack your structs, use tcpip host to network, and network to host to marshal data in and out, save state as ascii, never trust a float and don’t get creative and it’ll “just work“.
- gallier2 6y agoRarely used feature that I quite like is defining function types. typedef bool foo_fn(int, char); This declares a type of a function. foo_fn foo1, foo2, foo3, foo4; This declares 4 functions of the same type. It's equivalent to bool foo1(int, char); bool foo2(int, char); bool foo3(int, char); bool foo4(int, char); Unfortunately the type can not be used at function definition but that is not where function are interesting. They are neat for function pointers especially those that require casting. void function_taking_foo(int, foo_fn *); function_taking_foo(1, foo1); // no cast necessary as type is identical and even instead of function_taking_foo(1, (bool(*)(int,char))foo1); When you have a lot function pointers it is incredibly more readable than the usual syntax.
- gallier2 6y agoForeach macros. Nice when you have a list of constant that you need for declaring a lot of tables or enumerations. Here an example with ISO-639 language codes Example with 3 "values". This is the base definition from which all the tables and enums are produced. #define FOREACH_LAN(LAN)\ LAN(GA, IE, C_ANSI ) /**< Irish Gaelic : 0 */ \ LAN(DE, DE, C_ANSI ) /**< German : 1 */ \ LAN(DA, DA, C_ANSI ) /**< Danish : 2 */ \ LAN(EL, EL, C_GREEK ) /**< Greek : 3 */ \ LAN(EN, GB, C_ANSI ) /**< English : 4 */ \ LAN(ES, ES, C_ANSI ) /**< Spanish : 5 */ etc. Let's define an enum indexing with these languages #define GENERATE_LANIDX(lan,country,codepage) LANIDX_ ## lan, typedef enum { LANIDX_UNDEFINED = -1, // FOREACH_LAN(GENERATE_LANIDX) LANIDX_MAX // Automatically get the upper bound } LANIDX_TYPE; This is equivalent to typedef enum { LANIDX_UNDEFINED = -1, // LANIDX_GA, LANIDX_DE, LANIDX_DA, LANIDX_EL, LANIDX_EN, LANIDX_ES, LANIDX_MAX // Automatically get the upper bound } LANIDX_TYPE; but I didn't need to repeat all the language codes, the macro did it for us. Now in the module I can define tables, also without needing to repeat the codes const char *LanIdx2LanTable[] = { #define GENERATE_LANIDDX_2_LAN(lan,country,codepage) [1+ LANIDX_ ## lan]={#lan}, FOREACH_LAN(GENERATE_LANIDDX_2_LAN) }; const char *LanIdx2CodePageTable[] = { #define GENERATE_LANCP(lan,country,codepage) [1+LANIDX_ ## lan]=codepage, FOREACH_LAN(GENERATE_LANCP) };