33

Alternative titles (to aid search)

  • Convert a preprocessor token to a string
  • How can I make a char string from a C macro's value?

Original Question

I would like to use C #define to build literal strings at compile time.

The string are domains that change for debug, release, etc.

I would like to do something like this:

#ifdef __TESTING
    #define IV_DOMAIN example.org            // In house testing
#elif __LIVE_TESTING
    #define IV_DOMAIN test.example.com       // Live testing servers
#else
    #define IV_DOMAIN example.com            // Production
#endif

// Subdomain
#define IV_SECURE "secure.IV_DOMAIN"         // secure.example.org, etc.
#define IV_MOBILE "m.IV_DOMAIN"

But the preprocessor doesn't evaluate anything within ""

  1. Is there a way around this?
  2. Is this even a good idea?
1

7 Answers 7

41

In C, string literals are concatenated automatically. For example,

const char * s1 = "foo" "bar";
const char * s2 = "foobar";

s1 and s2 are the same string.

So, for your problem, the answer (without token pasting) is

#ifdef __TESTING
    #define IV_DOMAIN "example.org"
#elif __LIVE_TESTING
    #define IV_DOMAIN "test.example.com"
#else
    #define IV_DOMAIN "example.com"
#endif

#define IV_SECURE "secure." IV_DOMAIN
#define IV_MOBILE "m." IV_DOMAIN
Sign up to request clarification or add additional context in comments.

Comments

33

There are a couple of ways to do this:

  1. if you're dealing with only string literals, you can simply use simply use strings—placing one string literal after another causes the compiler to concatenate them.

  2. if there may be other things than string literals involved (i.e., you are creating new identifiers from the macros) use the '##" preprocessor token pasting operator. You'd probably also need to use the '#' 'stringizing operator to make your macros into literal strings.

An example of #1:

#ifdef __TESTING
    #define IV_DOMAIN "example.org"            // In house testing
#elif __LIVE_TESTING
    #define IV_DOMAIN "test.example.com"       // Live testing servers
#else
    #define IV_DOMAIN "example.com"            // Production
#endif

// Subdomain
#define IV_SECURE "secure." IV_DOMAIN          // secure.example.org, etc.
#define IV_MOBILE "m." IV_DOMAIN

And as far as the token pasting operator goes, I don't think that most of the answers that suggested using the token pasting preprocessor operator have actually tried it—it can be tricky to use.

Using the answer that is often suggested will result in a compiler error when you try to use the IV_SECURE macro, because:

#define IV_SECURE "secure."##IV_DOMAIN

expands to:

"secure"example.org

You might want to try to use the '#`' 'stringizing' operator:

#define IV_SECURE "secure." #IV_DOMAIN

But that won't work because it only works on macro arguments—not just any old macro.

one thing to be aware of when you're using the token-paste ('##') or stringizing ('#') preprocessing operators is that you have to use an extra level of indirection for them to work properly in all cases.

If you don't do this and the items passed to the token-pasting operator are macros themselves, you'll get results that are probably not what you want:

#include <stdio.h>

#define STRINGIFY2( x) #x
#define STRINGIFY(x) STRINGIFY2(x)
#define PASTE2( a, b) a##b
#define PASTE( a, b) PASTE2( a, b)

#define BAD_PASTE(x,y) x##y
#define BAD_STRINGIFY(x) #x

#define SOME_MACRO function_name

int main()
{
    printf( "buggy results:\n");
    printf( "%s\n", STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( BAD_PASTE( SOME_MACRO, __LINE__)));
    printf( "%s\n", BAD_STRINGIFY( PASTE( SOME_MACRO, __LINE__)));

    printf( "\n" "desired result:\n");
    printf( "%s\n", STRINGIFY( PASTE( SOME_MACRO, __LINE__)));
}

The output:

buggy results:
SOME_MACRO__LINE__
BAD_PASTE( SOME_MACRO, __LINE__)
PASTE( SOME_MACRO, __LINE__)

desired result:
function_name21

So using your original IV_DOMAIN defines and the utility macros from above, you could do this to get what you want:

// Subdomain
#define IV_SECURE "secure." STRINGIFY( IV_DOMAIN)   //secure.domain.org etc
#define IV_MOBILE "m." STRINGIFY( IV_DOMAIN)

3 Comments

Peter, Michael, thanks so much for this wonderful description of Macro issues. Can you clarify the mechanism between PASTE and BAD_PASTE? It seems stringify will not do macro evaluation, but an intervening macro call will. But I thought it's the macro evaluation that does the substitution?
It's kind of hard to describe - you should study the example and actually run some tests with those examples too. But the fundamental thing is that the "stringify" and token paste operators work on macro arguments. If the operand to those operators isn't an argument, then the literal text is used. So the workaround is to use indirection where the stringify or token paste operation is done in its own macro that deals only with the arguments to that macro.
Another thing to be aware of is that the result of the ## token paste operation has to be a valid single token (many compilers don't care). GCC docs say: "two tokens that don’t together form a valid token cannot be pasted together. For example, you cannot concatenate x with + in either order. If you try, the preprocessor issues a warning and emits the two tokens. Whether it puts white space between the tokens is undefined. It is common to find unnecessary uses of ## in complex macros. If you get this warning, it is likely that you can simply remove the ##. "
9

I see lots of good and correct answers to your first question, but none to your second, so here's this: I think this is a terrible idea.

Why should you have to rebuild your software (particularly the release version) just to change the server name? Also, how will you know which version of your software points at which server? You'll have to build in a mechanism to check at runtime.

If it's at all practical on your platform, I recommend you load the domains/URLs from a configuration file. Only the smallest of embedded platforms may not be "practical" for that purpose :)

Comments

8

Strings that are next together are combined by the C compiler.

#define DOMAIN "example.com"
#define SUBDOMAIN "test." DOMAIN
const char *asCString = SUBDOMAIN;
NSString *asNSString = @SUBDOMAIN;

Comments

6

Try using the ## operator

#define IV_SECURE secure.##IV_DOMAIN

2 Comments

I think the OP was asking about string concatenation (#), not token pasting (##). I didn't downvote, though. The question is a little ambiguous.
Token pasting ('##') or stringizing ('#') will not work without quite a bit of additional stuff.
5

What you need are the # and ## operators, and automatic string concatenation.

The # preprocessing operator turns the macro parameter into a string. The ## operator pastes two tokens (such as macro parameters) together.

The possibility that comes to mind to me is

#define IV_DOMAIN domain.org
#define IV_SECURE(DOMAIN) "secure." #DOMAIN

which should change IV_SECURE to

#define IV_SECURE "secure." "domain.org"

which will automatically concatenate to "secure.domain.org" (assuming the phases of translation are the same in C as C++).

Please, please read the comments, which show how I've managed to get confused. Bear in mind that I am thoroughly experienced in C, although perhaps a touch rusty. I would delete this answer, but I thought I'd leave it as an example of how easy it is to get confused by the C preprocessor.

7 Comments

## doesn't paste strings, it pastes tokens. Subtle but important difference.
Unfortunately, the version of cpp running in my cygwin box does not understand the # operator as you describe it.
The # operator only works on macro parameters. I.e. #define IV_SECURE(DOMAIN) "secure." #DOMAIN Not much help in this case.
Um, yeah. I've changed that now. Thanks for the corrections.
There's still a problem - IV_SECURE(IV_DOMAIN) produces "secure." "IV_DOMAIN" not "secure." "domain.org"
|
3

As others have noted, use token pasting. You should also be aware that macro names like

__TESTING

are reserved in C (don't know about Objective C) for the implementation - you are not allowed to use them in your own code. The reserved names are anything containing double underscores and anything begining with an underscore and an uppercase letter.

1 Comment

Objective C is supposed to be a strict superset of C, and so this should be a valid notice.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.