7

I'm trying to compile some C++ code using your standard g++ compiler. However, rather than compiling from a file:

main.cpp:

#include <iostream>

int main(){
    std::cout << "Hello World!\n";
    return 0;
}

I would prefer to do something like

g++ ... "#include <iostream>\n int main(){ std::cout << \"Hello World!\n\"; return 0;}"

A previous post from stackoverflow showed that

echo "int main(){}" | gcc -Wall -o testbinary -xc++ -

works but I would like to know how it works and better yet, if there is a way to do this without the need to pipe the contents.

EDIT:

I'm doing run-time code generation where I need to generate a shared library and load the functions created.

I thought there would be a flag to tell the compiler "hey, I'm giving you the source code and not the file".

Thanks again for the help!

Community
  • 1
  • 1
Yuuta
  • 414
  • 2
  • 13
  • 1
    I am curious, why do you want to do that for, what is the problem you are trying to solve? – Shafik Yaghmour Jul 17 '13 at 14:47
  • 1
    Alternatively, you could pipe whatever strings you want into a file, and then compile that file: `echo "int main(){}" >> main.cpp; gcc main.cpp -Wall -o testbinary` – Suedocode Jul 17 '13 at 14:54
  • @ShafikYaghmour I'm doing some JIT compilation using g++ and would like to load the shared library that results from it. – Yuuta Jul 17 '13 at 15:13
  • @Aggieboy I understand the how piping works, what I don't understand is where the string get's inserted into the g++ command. As in, how could I have the same functionality as the piping line you put up without having to pipe the echo output? Plus, writing into a file slows things down during execution. – Yuuta Jul 17 '13 at 15:13
  • 1
    @Yuuta Can you provide more details about the scenario you are working with, I don't totally understand your doing JIT this way. A better explanation of what you are trying to do may get your a better approach. – Shafik Yaghmour Jul 17 '13 at 15:18
  • @Yuuta Actually I misspoke; the `>>` operation is called *redirection*, whereas the ` | ` operation is called *piping*. I've seen many people incorrectly refer to the `>>` operation as piping (as I just did). The question asks for an alternative to piping, and my answer would be redirection first. Secondly, it will probably just cache the file before actually writing the file. If you want a fast JIT though, I wouldn't recommend some hackish shell script to do it. – Suedocode Jul 17 '13 at 15:20
  • @ShafikYaghmour I edited the question a bit. I want to do some code generation at runtime. – Yuuta Jul 17 '13 at 15:33
  • @Aggieboy I'm planning on doing a system call in a C++ program to the compiler while passing the code contents rather than the file. However, it seems your option to create a file is probably a better option. – Yuuta Jul 17 '13 at 15:35
  • @Yuuta: I would be interested in understanding more why you generate C++ code on the fly, and what kind of generator is it.... – Basile Starynkevitch Jul 17 '13 at 20:53
  • @BasileStarynkevitch: When I see questions like this I think "he's implementing a plugin/addon system (like WoW), except he doesn't know why using C++ for this is a bad idea" – Mooing Duck Jul 17 '13 at 20:55
  • It is apparently not a plugin, but a generated plugin... And I disagree that using C++ for this is a bad idea. – Basile Starynkevitch Jul 17 '13 at 21:00
  • possible duplicate of [Pipe file contents in g++ to compile](http://stackoverflow.com/questions/16131984/pipe-file-contents-in-g-to-compile) – Ciro Santilli OurBigBook.com Sep 15 '14 at 19:47

6 Answers6

8

echo "int main(){}" | gcc -Wall -o testbinary -xc++ -

works but I would like to know how it works and better yet, if there is a way to do this without the need to pipe the contents.

Alternatively you can say (e.g. in a shell-script):

gcc -Wall -o testbinary -xc++ - << EOF
int main(){}
EOF
Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
2

The compiler reads from an input source - either the stdin or a file supplied. You need a pipe to supply something from elsewhere to the compiler. There is no other choice (and of course, some compilers may not have an option to read from stdin either)

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • Thanks for the clarification, is there another way to supply the source through stdin without having to pipe it in? – Yuuta Jul 17 '13 at 15:16
  • 2
    _'is there another way to supply the source through stdin without having to pipe it in?'_ You can use a local data input in the shell: `gcc -Wall -o testbinary << EOF` and in the following lines `main() {} ...`, and in the **last** line `EOF` – πάντα ῥεῖ Jul 17 '13 at 15:34
  • 1
    @g-makulik And don't forget the `-` argument to tell g++ that it is to read standard in. – James Kanze Jul 17 '13 at 15:55
2

You mention generating C (or C++) code on the fly and then compiling it and dlopen-ing it.

I'm doing the same in MELT (a domain specific language to extend GCC).

I don't see any valid reason to avoid putting the code in some (temporary) file. Writing even a million line of generated C or C++ lines is quite quick (in MELT, less than a few seconds, and most of that time is not the I/O!). Compiling that is much much slower (a few dozens of seconds at least), and the interest of generating C or C++ code is notably to take advantage of the optimizations provided by GCC (or some other compiler). Avoiding generating a file would win you just some milliseconds (you won't even be able to significantly measure the difference).

So just generate your file in some temporary *.c file (you could use hash or timestamp techniques to generate a unique file name), then have GCC compile it into some *.so (perhaps by fork-ing some make process, like I do in MELT), then remove that temporary file (perhaps using atexit).

BTW, this technique is practically compatible with human interaction on current PCs. MELT has a read-eval-print-loop which generates a new C++ file of a few hundred lines, compiles and dlopen-s it, on each interaction, and it is quite usable!

Avoiding the generation of the file is painful, and the gain is absolutely negligible. You might generate it in some tmpfs filesystem on Linux (e.g. in /tmp/). Most of the time would be spent by GCC compiling that file (especially if you compile with some optimization e.g. gcc -O -fPIC -shared somefile.c -o someplugin.so). The time to write it on disk (by your program) and to read it (and even parse it, for C) by GCC is negligible. Use the -ftime-report option of GCC to understand where GCC is spending its time, it is not in parsing as soon as your pass -O to GCC.

Some versions of GCC or of other C compilers might reject stdin as input; some C compilers might want to mmap the C source file (e.g. by using "rm" as mode for fopen on Glibc). In general, compiling something which is not a *.c file as C is non-standard, etc... So better avoid doing that, since the gain is negligible.

If you don't care at all about optimization and want quick compilation of C (not C++) into very slow machine code, consider using instead tinycc (at least on 32 bits machines, on 64 bits it could be buggy) which has a library able to compile some C code inside a string. tcc and its libtcc.a is able to compile very fast (more than 10x times GCC) some C code but the performance of the produced machine code is very bad (very slow, unoptimized code).

Once GCC has compiled your generated C code, you can of course remove the generated source code file. (and you could even also remove the .so after having dlopen-ed it).

BTW, you could use clang++ to compile the generated C++ file. And you might use LLVM (and generate internal LLVM representation, without using any file) instead.

See also this answer.

I'm doing run-time code generation where I need to generate a shared library and load the functions created.

Consider also using JIT-compiling libraries such as libgccjit, LLVM or asmjit. Look also into SBCL (which generates machine code at almost every REPL interaction). Of course, you need to understand the relevant calling conventions and ABI on your system. So read also this, elf(5) and Drepper's paper How To Write Shared Libraries

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • I don' understand - why is this true : ´Avoiding the generation of the file is painful, and the gain is absolutely negligible´. The generated code should be the same whether or not you create an intermediate file. – BЈовић Jul 30 '13 at 14:01
  • I slightly improved my answer. – Basile Starynkevitch Jul 30 '13 at 14:05
  • 1
    Just stumbled on this one. Piping code to the compiler without creating a temp file could be done for several reasons other than for (supposed) efficiency. Privacy: the code could be automatically generated by another tool and contain some sensitive data that we don't want to be saved on disk (not even on a temp file). Perhaps you started the tool from a pendrive where not even file "shredding" would work because of HW wear leveling mechanism (and you fear possible low-level recovery). .. – LorenzoDonati4Ukraine-OnStrike Aug 08 '20 at 13:06
  • 2
    ... Another one: avoiding write access to a medium. Maybe we started a tool from a location where you don't have write access to any medium (Live CD?), maybe for rescue or forensic reasons. Niche use cases, sure, but still relevant. – LorenzoDonati4Ukraine-OnStrike Aug 08 '20 at 13:07
1

How about popen("gcc -o -xc++ -", "w");? Gives you aFILE*` but the output goes straight into GCC.

BTW, there is no point in using the -Wall flag. That's for human consumption. In fact, -w -Wfatal-errors makes sense. Either it compiles or it doesn't.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 1
    Well, using `-Wall` flag for machine generated C or C++ code is very helpful, at least to debug the generator! Generated code should give no warnings... – Basile Starynkevitch Jul 17 '13 at 21:10
1

-x is needed to specific the language you will compile.

here is an example:

gcc -x c - <<eof
#include <stdio.h>

void foo()
{
    int a = 10;
    static int sa = 10;

    a += 5;
    sa += 5;

    printf("a = %d, sa = %d\n", a, sa);
}


int main()
{
    int i;

    for (i = 0; i < 10; ++i)
        foo();
}
eof
jianyongli
  • 1,131
  • 12
  • 10
0

On Unix you can automate all process creating C/C++ programs by usage shell.

1.Create file (example run_c.sh)

2.Paste code to this file

#!/bin/bash

# Generate random filename
RANDOM_FILENAME=`cat /dev/urandom | tr -dc 'A-Za-z0-9' | fold -w 20 | head -n 1`
RANDOM_FILENAME=_$RANDOM_FILENAME
readonly RANDOM_FILENAME

# random filename for a C file
RANDOM_FILENAME_C=$RANDOM_FILENAME.c
readonly RANDOM_FILENAME

# catch stdin as code
CODE=$1

# names headers of the C standart language
declare -a C_STANDART_LIBRARY=('stdio' 'errno' 'assert' 'math' 'stdarg' 'stdbool' 'stdlib' 'string' 'time')

# create the C file
touch $RANDOM_FILENAME_C

# write a first line to the file
printf '// Compile C code from stdin\n\n' >> $RANDOM_FILENAME_C

# make include all headers to the file
for header in "${C_STANDART_LIBRARY[@]}"
do
    printf '#include <%s.h>\n' $header >> $RANDOM_FILENAME_C
done

# make include all headers to the file
printf "\nint main(int argc, char *argv[]) {\n" >> $RANDOM_FILENAME_C

# split the code from stdin by ';' to an array lines
IFS=';' read -r -a LINES_CODE <<< "$CODE"

# write line by line the code
for line in "${LINES_CODE[@]}"
do
    printf '%s;\n' "$line" | sed -e 's/^[[:space:]]//' | sed -e 's/^/\t/' >> $RANDOM_FILENAME_C
done

# write ending the function 'main'
printf "\treturn 0;\n}\n" >> $RANDOM_FILENAME_C

# uncomment for display the C code
# cat $RANDOM_FILENAME_C

# compile the file
gcc -Wall -std=c11 -o $RANDOM_FILENAME $RANDOM_FILENAME_C

# run programm if no errors
if [ -f "$RANDOM_FILENAME" ];
    then
    ./$RANDOM_FILENAME
fi

# rm the file with source code
rm $RANDOM_FILENAME_C
# rm the compliled file

if [ -f "$RANDOM_FILENAME" ];
    then
        rm $RANDOM_FILENAME
fi

3.Make this file as executable

$ chmod +x run_c.sh 
$ ls -l | grep run_c.sh 
-rwxr-xr-x 1 setivolkylany setivolkylany 1589 Dec 26 11:19 run_c.sh

Usage:

$ ./run_c.sh 'int a = 4; int b = 6; int c = a + b; printf("%d + %d = %d\n", a, b, c)'
4 + 6 = 10

$ ./run_c.sh 'puts("Worked");'
Worked

Testing environment

$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description:    Debian GNU/Linux 8.6 (jessie)
Release:    8.6
Codename:   jessie
$ uname -a
Linux localhost 3.16.0-4-amd64 #1 SMP Debian 3.16.36-1+deb8u2 (2016-10-19) x86_64 GNU/Linux

Inspired from http://django-notes.blogspot.com/2013/01/compiling-c-from-stdin.html and https://github.com/vgvassilev/cling

PADYMKO
  • 4,217
  • 2
  • 36
  • 41