How to create string literal from -D compiler defined variable of a Windows path
Asked Answered
C

2

7

Under Windows, I have an environment variable that contains a Windows-style path. I'd like to build that path into my program and print it out. So if my path is c:\top, I pass it into the compiler using -DTOP=$(TOP). Note I cannot convert it to c:\\top before I pass it into the compiler.

Right now, I have the equivalent of:

#define TOP=c:\top

I want the equivalent of:

char path[]="c:\\top";

I can't just use the stringafication operator:

#define WRAP1(X) #X
#define WRAP(X) WRAP1(X)
char path[] = WRAP(TOP);

That just results in the string "c:\top" which the compiler views as an escape sequence (i.e. \t).

I think a possible solution would be to construct a string literal, but other solutions will be also be fine. Is there a way to use macros to construct a string literal that would yield:

char path[] = R"foo(c:\top)foo";

So far, all my attempts have failed for reasons involving the variations of the " or ( ) or the \ .

Thanks.

Charpentier answered 2/7, 2015 at 20:27 Comment(8)
"Note I cannot convert it to c:\\top before I pass it into the compiler" - Why?Kanarese
Why not -DTOP=R"foo($(TOP))foo" instead? Edit: The quotes may need to be escaped since this is a command line option, like so: -DTOP="R""foo($(TOP))foo"""Quicksilver
I am in a custom compiling environment that I cannot alter. I cannot use additional quotes when I specify the the -D. The compiling environment is also using quotes to parse additional aguements to the compiler.Charpentier
Have you tried a forward slash instead of a backslash?Coral
A forward slash would work if I could change it before I passed it into the compilter, but as I said, I can't convert the string before passing it in. The string I have to work with is c:\top. It is what it is.Charpentier
The correct answer is to inform the build environment person/team that you need it changed. Don't be scared of "it can't be done" scenarios and get it fixed where it's broke - i.e. not being able to quote anything, or change anything on a command line! Crazy!Baluchistan
Not being able to change anything on a command line? Well, I guess I can understand from a security point of view, but if that was really important here, Windows likely wouldn't be used like this (it's obviously not meant for this sort of secure flexibility out of the box). Anyway, I'd do as @BrianSidebotham suggested in his comment.Subclavian
That is one answer, and yes, probably the one I will be exploring. But I want to know if there is an actual way to do this using some combination of macros and what not that I just haven't been clever enough to think of yet.Charpentier
B
2

You can convert your defined path to a string by prefixing it with the stringizing operator #. However, this only works in macros. You actually need a double-macro to make it work properly, otherwise it just prints TOP. Also placing the pathname in quotes is important - oh the example has the path stored under the env PathDirName

Defining the path for the compiler -

/DTOP="\"$(PathDirName)\\""

Using within the code

#define STRINGIZE2(x) #x
#define STRINGIZE(x) STRINGIZE2(x)

char path[] = STRINGIZE(TOP);

This has worked for me. You nearly had it working, so hope this helps.

[EDIT] I can see the problem now - within C:\top - its taking the 'backslash t' as a control code - '\t'. This appoarch is becoming a little bit of a nightmare to work out - as you really need to create the file name with two slashes or use forward slashes. I do feel I have confused issues here by answering before reviewing fully what has happened. I have tried several methods - but not being able to change the passed in define - I can only suggest using a regex library or manual scanning the string - replacing the control charactors with the correct '\' letter.

I've knocked up an example showing this just with the '\t' in your example - It's not nice code, it's written to explain what is being done, hopefully it gives an visual example and it does (in a not so nice way) sort out the ONE issue you are having with 'C:\top' .. as I have said - if using this cough, method, you will need to handle all control codes. :-)

char stringName[256];
char* pRefStr = STRING(TOP);
char* pDestStr = stringName;
int nStrLen = strlen( pRefStr );

for( int nIndex = 0; nIndex < nStrLen; nIndex++ )
{
    if ( *pRefStr == '\t' )
    {
        *pDestStr++ = '\\';
        *pDestStr++ = 't';
        pRefStr++;
    }
    else
    {
        *pDestStr++ = *pRefStr++;
    }
}
*pDestStr = '\0';

Once again - sorry for any confusion - I've left my answer here as reference for you - and hopefully someone will come up with a way of handling the define-string (with the control charactors).

thanks, Neil

Bircher answered 2/7, 2015 at 20:55 Comment(4)
Thanks, but again, I can't change what is passed in. I was wondering if I could use the concat string operators ## someway to construct the string literal.Charpentier
Hmmm as it's a macro - you could try adding quotes before and after TOP. I cannot try that as I'm away from my dev machine - so it should work. One way coud be using format(..) - If I get near my machine I'll have a go at trying this.Bircher
I've edited my answer shol74, not good news as I have been unable to figure out how NOT to get the '\t' charactor when getting the TOP define. As I mentioned - hopefully someone will be able to work this out. I serious would try and change the define that's passed in.Bircher
Thanks Neil. My actual problem is slightly worse though. The C:\top was actually shortened for my example. The \t will at least compile. My actual path has a \o in it which won't. So unfortunately, it's also not even a matter of just a string replacement either. Thanks for your time though.Charpentier
B
0

The rather bizarre syntax you're looking for performs on-the-fly substring replacement in variable expansion, as well as some quote mark escaping to make the definition a string. I found the substring replacement information here.

set DIR=C:\WINDOWS gets the env var set, and then we have a test prog:

#include <stdio.h>

#define STR(x)    #x
#define STRING(x) STR(x)      

int main( int argc, char* argv[] )
{
    printf( "DIR: %s\n", STRING(DIR) );
    return 0;
}

As you cannot quote in the shell, you can stringize here, but you must still do the variable substring replacement.

Pass the env var in through cmd.exe:

gcc -Wall -DDIR=%DIR:\=\\% main.c

See the link above for more information, or google substring replacement. I can't find a link to any Microsoft info on the function (what a surprise!)

Baluchistan answered 2/7, 2015 at 20:55 Comment(4)
Again, I cannot alter the string before passing it in due to a custom build environment that is already using quote characters. So adding any quote characters there just terminates my string early there. I need to work with the #define top c:\top in the .cpp file.Charpentier
@Charpentier I've edited to remove the quote marks from the compile command line. Seems like a strange requirement. As Neil noted, you can just stringize it using macro expansion.Baluchistan
@Charpentier I think you're asking for the impossible because there is no sensible encoding for the preprocessor to work with, and the preprocessor is the only tool you have.Paganini
Yeah, I do only have the preprocessor. That's why I was hoping there might be some clever way to concatenate the R"(c:\top)" to it and make it a string literal.Charpentier

© 2022 - 2024 — McMap. All rights reserved.