Search code examples
gcccompilationg++systemcommand-line-arguments

Can you pass your code directly into gcc? For example: gcc -? 'int main(){return 0;}'


Can you pass your code directly into gcc? If so what is the command line option for it?

For example:

g++ -? 'int main(){return 0;}'

I need to know because I am using a system command and I rather not make files:

system("g++ -C "+code_string+" -o run.out");

Basile Starynkevitch solution worked, however I am getting compile errors when I use newlines:

echo '#include\nint main(){printf("Hello World"); return 0;}' | g++ -x c++ -Wall -o myprog /dev/stdin

Edit: fixed it

echo -e '#include\nint main(){printf("Hello World"); return 0;}' | g++ -x c++ -Wall -o myprog /dev/stdin

Solution

  • You could ask GCC to read from stdin. Read the Invoking GCC chapter of its documentation. Use its -x option with /dev/stdinor with -:

     echo 'int main(){return 0;}' | g++ -x c++ -O -Wall -o myprog /dev/stdin
    

    BTW, since int main(){return 0;} is a valid C program, you could use

     echo 'int main(){return 0;}' | gcc -x c -O -Wall -o myprog -
    

    Programatically, you should consider using popen(3) to get a some FILE* handle for a pipe(7) (so FILE* f = popen("g++ -x c++ -O -Wall -o myprog /dev/stdin", "w"); then check that f is not null) and fprintf into it then pclose it at last. Don't forget to test the status of pclose.

    However, most of the time spent by GCC is not parsing (use -ftime-report developer option to find out). You often want to ask it to optimize (with -O2 -march=native or just -O for example), and you surely want to ask for all warnings (with at least -Wall and perhaps also -Wextra).

    If you want to produce some plugin code in /tmp/someplugin.so from some emitted C++ code in /tmp/myemitted.cc to be dynamically loaded on Linux, compile it as position-independent code into a shared object dynamic library with e.g.

    g++ -o /tmp/someplugin.so -fPIC -shared -Wall -O /tmp/myemitted.cc
    

    etc.... then use dlopen(3) on /tmp/someplugin.so with dlsym(3) to fetch some loaded symbols. My GCC MELT is doing this.

    Since parsing time is negligible, you could instead write C or C++ code in some temporary file (inside /tmp/ or /run which is often some fast tmpfs on most Linux systems, so writing into it does not require disk I/O).

    At last, recent GCC (use at least GCC 6) also has GCCJIT (actually libgccjit). You could use it to build some representation of generated code then ask GCC to compile it.

    See also this and that. Read the C++ dlopen mini howto and the Program Library HowTo, and Drepper's How To Write Shared Libraries

    I rather not make files

    Generating a temporary file (see mkstemp(3) etc... and you practically could also general some random file name under /tmp/ ending with .c, then register its removal with atexit(3) passed some function doing unlink(2)...) is really quick (but you should build some kind of AST in memory before emitting C++ or C code from it). And using some Makefile to compile the generated code with some make command has the advantage (for the advanced user) to be able to change compilers or options (by editing that Makefile to configure make).

    So you are IMHO wrong in avoiding temporary files (notice that gcc & g++ are also generating and deleting temporary files, e.g. containing some assembler code). I would suggest on the contrary generating a temporary file (matching /tmp/mytemp*.cc) using some random numbers (see random(3); don't forget to seed the PRNG with e.g. srandom(time(NULL)); early in your main). It could be as simple as

    char tmpbuf[80];
    bool unique;
    do { // in practice, this loop is extremely likely to run once
      snprintf(tmpbuf, sizeof(tmpbuf), "/tmp/mytemp_%lx_p%d.cc", 
               random(), (int)getpid());
      unique = access(tmpbuf, F_OK);
    } while (unique);
    // here tmpbuf contains a unique temporary file name
    

    You coded:

    system("g++ -C "+code_string+" -o run.out");

    Beware, + is usually not string catenation. You might use snprintf(3) or asprintf(3) to build strings. Or use in C++ std::string. And if you use system(3) you should check its return code:

    char cmdbuf[128];
    snprintf(cmdbuf, sizeof(cmdbuf), "g++ -Wall -O %s -o run.out", tmpbuf);
    fflush(NULL);
    if (system(cmdbuf) != 0) {
       fprintf(stderr, "compilation %s failed\n", cmdbuf);
       exit(EXIT_FAILURE);
    }
    

    BTW, your example is wrong (missing <stdio.h>); it is C code, not C++ code. It should be

    echo -e '#include <stdio.h>\nint main(){printf("Hello World"); return 0;}' \
         |  gcc -x c -Wall -O -o myprog -
    

    PS. My answer is focused on Linux, but you could adapt it for your OS.