【问题标题】:Compiling C++-code without a file在没有文件的情况下编译 C++ 代码
【发布时间】:2013-07-16 04:40:16
【问题描述】:

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

main.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 的 post 表明

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

工作,但我想知道它是如何工作的,如果有办法做到这一点而不需要管道内容。

编辑:

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

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

再次感谢您的帮助!

【问题讨论】:

  • 我很好奇,您为什么要这样做,您要解决的问题是什么?
  • 或者,您可以将所需的任何字符串通过管道传输到文件中,然后编译该文件:echo "int main(){}" &gt;&gt; main.cpp; gcc main.cpp -Wall -o testbinary
  • @ShafikYaghmour 我正在使用 g++ 进行一些 JIT 编译,并希望加载由此产生的共享库。
  • @Aggieboy 我了解管道的工作原理,但我不明白字符串 get 插入 g++ 命令的位置。如在,我怎样才能拥有与您放置的管道相同的功能而无需管道回声输出?另外,写入文件会在执行过程中减慢速度。
  • @Yuuta 您能否提供有关您正在使用的场景的更多详细信息,我不完全理解您以这种方式执行 JIT。对您正在尝试做的事情进行更好的解释可能会为您提供更好的方法。

标签: c++ compilation g++


【解决方案1】:

需要-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

【讨论】:

    【解决方案2】:

    您提到即时生成 C(或 C++)代码,然后编译它并dlopen-ing 它。

    我在 MELT(一种扩展 GCC 的领域特定语言)中也这样做了。

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

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

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

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

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

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

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

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

    另见this answer

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

    考虑同时使用JIT-compiling 库,例如libgccjitLLVMasmjit。还要查看SBCL(几乎在每次REPL 交互时都会生成机器代码)。当然,你需要了解你系统上相关的calling conventionsABI。所以也请阅读thiself(5) 和 Drepper 的论文How To Write Shared Libraries

    【讨论】:

    • 我不明白 - 为什么这是真的:'避免生成文件是痛苦的,而收益绝对可以忽略不计'。无论是否创建中间文件,生成的代码都应该相同。
    • 我的答案略有改进。
    • 刚刚偶然发现了这个。除了(假设的)效率之外,可以出于多种原因在不创建临时文件的情况下将代码传递到编译器。隐私:代码可以由另一个工具自动生成,并包含一些我们不想保存在磁盘上的敏感数据(甚至不是临时文件)。也许您是从 Pendrive 启动该工具的,由于硬件磨损均衡机制,甚至文件“粉碎”都无法工作(并且您担心可能的低级恢复)。 ..
    • ... 另一个:避免对介质的写访问。也许我们从一个您对任何介质(Live CD?)没有写入权限的位置启动了一个工具,可能是出于救援或取证的原因。利基用例,当然,但仍然相关。
    【解决方案3】:

    在 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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 好吧,对机器生成的 C 或 C++ 代码使用-Wall 标志非常有帮助,至少可以调试生成器!生成的代码应该没有警告...
      【解决方案5】:

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

      工作,但我想知道它是如何工作的,如果有办法做到这一点而不需要管道内容。

      您也可以说(例如在 shell 脚本中):

      gcc -Wall -o testbinary -xc++ - << EOF
      int main(){}
      EOF
      

      【讨论】:

      • 哦,您的替代解决方案正是我想要的(所以我不必创建文件)。非常感谢!
      • 使用 Bash shell 有一种更简单的方法来创建“此处文档”,使用 &lt;&lt;&lt; 而不是 &lt;&lt;EOF 例如g++ -x c++ - &lt;&lt;&lt; "int main() { }"
      • 魔术位是最后的-,它告诉gcc 吃掉来自标准输入的输入。
      【解决方案6】:

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

      【讨论】:

      • 感谢您的澄清,是否有另一种方法可以通过标准输入提供源代码而无需通过管道输入?
      • '是否有另一种方法可以通过 stdin 提供源而无需通过管道输入?' 您可以在 shell 中使用本地数据输入:gcc -Wall -o testbinary &lt;&lt; EOF 和以下行main() {} ...,在最后EOF
      • @g-makulik 不要忘记 - 参数告诉 g++ 它是读取标准。
      猜你喜欢
      • 2016-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-05
      • 2022-12-04
      • 1970-01-01
      相关资源
      最近更新 更多