13

Ignoring that there are sometimes better non-macro ways to do this (I have good reasons, sadly), I need to write a big bunch of generic code using macros. Essentially a macro library that will generate a large number of functions for some pre-specified types.

To avoid breaking a large number of pre-existing unit tests, one of the things the library must do is, for every type, generate the name of that type in all caps for printing. E.g. a type "flag" must be printed as "FLAG".

I could just manually write out constants for each type, e.g.

#define flag_ALLCAPSNAME FLAG

but this is not ideal. I'd like to be able to do this programatically.

At present, I've hacked this together:

char capname_buf[BUFSIZ];
#define __MACRO_TO_UPPERCASE(arg) strcpy(capname_buf, arg); \
 for(char *c=capname_buf;*c;c++)*c = (*c >= 'a' && *c <= 'z')? *c - 'a' + 'A': *c;
__MACRO_TO_UPPERCASE(#flag)

which does what I want to some extent (i.e. after this bit of code, capname_buf has "FLAG" as its contents), but I would prefer a solution that would allow me to define a string literal using macros instead, avoiding the need for this silly buffer.

I can't see how to do this, but perhaps I'm missing something obvious?

I have a variadic foreach loop macro written (like this one), but I can't mutate the contents of the string literal produced by #flag, and in any case, my loop macro would need a list of character pointers to iterate over (i.e. it iterates over lists, not over indices or the like).

Thoughts?

Community
  • 1
  • 1
John Doucette
  • 4,370
  • 5
  • 37
  • 61
  • possible duplicate of [cant convert lowercase to uppercase a string by preprocessor directive](http://stackoverflow.com/questions/3216049/cant-convert-lowercase-to-uppercase-a-string-by-preprocessor-directive) – unwind Sep 17 '12 at 08:32
  • @unwind I saw that, but the answer is not correct (and thus not useful) b/c it assumes that you cannot make a loop in a preprocessor (you can). – John Doucette Sep 17 '12 at 08:45
  • How? Your example doesn't show a loop done with the preprocessor, and if you know how to do that, shouldn't that be part of your starting point? – unwind Sep 17 '12 at 09:32
  • Probably a significant restriction is that C macros have no means to split a token into smaller tokens, only to paste or stringize them to generate larger tokens. So the preprocessor cannot act on the individual characters of your type name `flag`. There are other (more powerful) preprocessors for C available, though, aside from what the standard requires. – Steve Jessop Sep 17 '12 at 10:26
  • @unwind I've added a link to code the is quite similar to my macro loop. The reason I do not include it is that it is both very long, and not relevant unless there's a way to split tokens, which @Steve\ Jessop indicates there is not. – John Doucette Sep 17 '12 at 10:32
  • 2
    Possible duplicate of [Can you capitalize a pasted token in a macro?](https://stackoverflow.com/questions/3400386/can-you-capitalize-a-pasted-token-in-a-macro) – Ciro Santilli OurBigBook.com Aug 05 '17 at 22:18
  • Since at least C++11, probably requires C++14, you can use templates like so: https://gist.github.com/texus/8d867996e7a073e1498e8c18d920086c — It's not preprocessor, but it gets compiled in the case you want so for example you could have a macro letting you enter a name "FooBlah" and in a table save "fooblah" or "FOOBLAH". – Alexis Wilke Apr 23 '19 at 02:50

2 Answers2

17

It is not possible in portable C99 to have a macro which converts a constant string to all uppercase letters (in particular because the notion of letter is related to character encoding. An UTF8 letter is not the same as an ASCII one).

However, you might consider some other solutions.

  • customize your editor to do that. For example, you could write some emacs code which would update each C source file as you require.

  • use some preprocessor on your C source code (perhaps a simple C code generator script which would emit a bunch of #define in some #include-d file).

  • use GCC extensions to have perhaps

    #define TO_UPPERCASE_COUNTED(Str,Cnt)
    #define TO_UPPERCASE(Str) TO_UPPERCASE_COUNTED(Str,__COUNT__) {( \
       static char buf_##Cnt[sizeof(Str)+4]; \
       char *str_##Cnt = Str; \
       int ix_##Cnt = 0; \
       for (; *str_##Cnt; str_##Cnt++, ix_##Cnt++) \
         if (ix_##Cnt < sizeof(buf_##Cnt)-1) \
             buf_##Cnt[ix_##Cnt] = toupper(*str_##Cnt); \
       buf_##Cnt; )}
    
  • customize GCC, perhaps using MELT (a domain specific language to extend GCC), to provide your __builtin_capitalize_constant to do the job (edit: MELT is now an inactive project). Or code in C++ your own GCC plugin doing that (caveat, it will work with only one given GCC version).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
1

It's not possible to do this entirely using the c preprocessor. The reason for this is that the preprocessor reads the input as (atomic) pp-tokens from which it composes the output. There's no construct for the preprocessor to decompose a pp-token into individual characters in any way (no one that would help you here anyway).

In your example when the preprocessor reads the string literal "flag" it's to the preprocessor basically an atomic chunk of text. It have constructs to conditionally remove such chunks or glue them together into larger chunks.

The only construct that allows you in some sense to decompose a pp-token is via some expressions. However these expressions only can work on arithmetic types which is why they won't help you here.

Your approach circumvents this problem by using C language constructs, ie you do the conversion at runtime. The only thing the preprocessor does then is to insert the C code to convert the string.

skyking
  • 13,817
  • 1
  • 35
  • 57