John Doucette
John Doucette

Reputation: 4540

Is it possible to convert a C string literal to uppercase using the preprocessor (macros)?

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?

Upvotes: 14

Views: 20191

Answers (2)

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).

Upvotes: 19

skyking
skyking

Reputation: 14395

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.

Upvotes: 1

Related Questions