6

我正在尝试使用您的标准 g++ 编译器编译一些 C++ 代码。但是,不是从文件编译:

主.cpp:

#include <iostream>

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

我宁愿做类似的事情

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

stackoverflow之前的一篇文章表明

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

工作,但我想知道它是如何工作的,而且更好的是,如果有一种方法可以做到这一点而无需管道内容。

编辑:

我正在生成运行时代码,我需要生成一个共享库并加载创建的函数。

我以为会有一个标志告诉编译器“嘿,我给你的是源代码而不是文件”。

再次感谢您的帮助!

4

6 回答 6

7

回声“int main(){}”| gcc -Wall -o testbinary -xc++ -

工作,但我想知道它是如何工作的,而且更好的是,如果有一种方法可以做到这一点而无需管道内容。

或者,您可以说(例如在 shell 脚本中):

gcc -Wall -o testbinary -xc++ - << EOF
int main(){}
EOF
于 2013-07-17T15:44:19.557 回答
2

编译器从输入源读取 -stdin或提供的文件。您需要一个管道来从其他地方向编译器提供一些东西。没有其他选择(当然,某些编译器可能没有读取stdin任何一个选项)

于 2013-07-17T14:52:46.183 回答
2

您提到即时生成 C(或 C++)代码,然后对其进行编译和dlopen-ing。

我在MELT(一种扩展 GCC 的领域特定语言)中做同样的事情。

我没有看到任何避免将代码放入某个(临时)文件的正当理由。甚至编写一百万行生成的 C 或 C++ 行都非常快(在 MELT 中,不到几秒钟,而且大部分时间都不是 I/O!)。编译要慢得多(至少几十秒),生成 C 或 C++ 代码的兴趣主要是利用 GCC(或其他一些编译器)提供的优化。避免生成文件只会为您赢得几毫秒(您甚至无法显着衡量差异)。

所以只需在一些临时*.c文件中生成你的文件(你可以使用哈希或时间戳技术来生成一个唯一的文件名),然后让 GCC 将它编译成一些文件*.so(也许通过fork-ing 一些make过程,就像我在 MELT 中所做的那样),然后删除它临时文件(可能使用atexit)。

顺便说一句,这种技术实际上与当前 PC 上的人机交互兼容。MELT 有一个 read-eval-print-loop,它生成一个几百行的新 C++ 文件,在每次交互时编译和 dlopen-s 它,它非常有用!

避免文件的生成是痛苦的,收益绝对可以忽略不计。tmpfs您可能会在 Linux 上的某些文件系统中生成它(例如 in /tmp/)。大部分时间将花在 GCC 编译该文件上(特别是如果您使用一些优化进行编译,例如gcc -O -fPIC -shared somefile.c -o someplugin.so)。GCC 将它写入磁盘(通过您的程序)和读取它(甚至解析它,对于 C)的时间可以忽略不计。使用 GCC 的-ftime-report选项来了解 GCC 将时间花在哪里,它不会在您传递-O给 GCC 后立即进行解析。

某些版本的 GCC 或其他 C 编译器可能拒绝stdin作为输入;一些 C 编译器可能想要mmapC 源文件(例如,通过在 Glibc 上使用"rm"as 模式)。fopen通常,将不是*.c文件的东西编译为C是非标准的,等等......所以最好避免这样做,因为增益可以忽略不计。

如果您根本不关心优化并希望将 C(不是 C++)快速编译成非常慢的机器代码,请考虑使用具有库的tinycc(至少在 32 位机器上,在 64 位机器上可能有问题)能够在字符串中编译一些 C 代码。tcclibtcc.a能够非常快地编译一些 C 代码(超过 GCC 的 10 倍),但生成的机器代码的性能非常差(非常慢,未优化的代码)。

一旦 GCC 编译了您生成的 C 代码,您当然可以remove生成生成的源代码文件。(你甚至可以在-ed.so之后删除它)。dlopen

顺便说一句,您可以clang++用来编译生成的 C++ 文件。您可能会使用 LLVM(并生成内部 LLVM 表示,而不使用任何文件)。

另请参阅此答案

我正在生成运行时代码,我需要生成一个共享库并加载创建的函数。

还可以考虑使用JIT 编译库,例如libgccjitLLVMasmjit。还可以查看SBCL(几乎在每次REPL交互时都会生成机器代码)。当然,您需要了解系统上的相关调用约定ABI。所以还要阅读这个elf(5)和 Drepper 的论文如何编写共享库

于 2013-07-17T20:45:26.353 回答
1

popen("gcc -o -xc++ -", "w"); FILE*`怎么样,? Gives you a但输出直接进入 GCC。

-Wall顺便说一句,使用标志没有意义 。那是供人食用的。事实上,-w -Wfatal-errors有道理。要么编译要么不编译。

于 2013-07-17T21:02:22.103 回答
0

在 Unix 上,您可以通过使用 shell 自动化所有创建 C/C++ 程序的过程。

1.创建文件(例如run_c.sh)

2.粘贴代码到这个文件

#!/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.将此文件设为可执行文件

$ 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

用法:

$ ./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

测试环境

$ 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

灵感来自http://django-notes.blogspot.com/2013/01/compiling-c-from-stdin.htmlhttps://github.com/vgvassilev/cling

于 2016-12-26T09:36:47.817 回答
0

-x需要指定您将编译的语言。

这是一个例子:

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
于 2020-12-23T13:23:50.943 回答