繁体   English   中英

从C ++程序构建C程序

[英]Build a C program from a C++ program

那么,我想做什么:基本上我想从另一个c ++程序(主程序)构建一个C / C ++程序(准备执行)。

我们来看这个例子:main.cpp

int main()
{
   //do something
   //there I will modify source of program which I want to build 
   sourceCode = ""; //maybe from a file
   //then a function like this:
   buildProgramFromSource(sourceCode, "program.exe");
}

我想从main.cpp生成的程序源

int main()
{
    //do another thing
    int value1 = value From main.cpp
    int value2 = value frim main.cpp too
}

所以,我有源存储在main.cpp上,我想修改该源然后构建该程序。

我该如何实现呢?

或者,也许还有其他方法可以从主C ++程序生成.exe程序?

稍后编辑:假设环境没有安装任何编译器。 编译器应该集成在主程序中。

换句话说,我不想使用像环境一样使用编译器的system()shellexecute等函数。

该应用程序将在Windows下使用。

一个例子非常有用。

它是特定于操作系统的 如果第二个(构建的)程序是独立程序或库(或第一个加载的某些代码),则情况会有所不同。

在Linux(和其他Posix系统)上,您可以生成C或C ++文件(通过使用常用的文本输出函数;我建议在内存中构建生成的C ++文件的某些AST )。 然后你可以通过分支进程运行一些C ++编译器(在简单的情况下,使用popen(3)system(3) ,或使用低级fork(2)execve(2)等.... )。 请参阅高级Linux编程了解更多信息

然后你可以用dlopen(3)动态加载获得的共享对象,并使用dlsym检索一些符号(函数指针)

Linux细节

  • 使用-rdynamic编译和链接主程序,即g++ -Wall -g -rdynamic main.cc -o prog ; 需要-rdynamic标志以使程序函数从加载的代码中可见。 声明加载函数的签名并声明全局函数指针是很有用的,例如:

      typedef int somefun_t (const std::string&); somefun_t *globfun; 
  • 在运行时生成一个C ++文件genfoo.cc ,其函数由声明为extern "C"的主程序访问。

  • 将编译分成具有位置无关代码的共享对象( 共享库 ):从程序内部运行: g++ -Wall -fPIC -g -O -shared genfoo.cc -o genfoo.so

  • 从你的主程序中调用dlopendlsym (请注意,如果你传递一个没有任何/的文件名, dlopen做一些奇怪的事情):

      void* hdl = dlopen("./genfoo.so", RTLD_NOW); if (!hdl) { std::cerr << "dlopen:" << dlerror() << std::endl; exit(EXIT_FAILURE); }; globfun = dlsym(hdl, "func"); if (!globfun) { std::cerr << "dlsym func:" << dlerror() << std::endl; exit(EXIT_FAILURE); }; 
  • 根据需要使用函数指针globfun ,例如

     std::string s; int r = (*globfun) (s); 
  • 如果你是认真的,你应该在退出之前dlclose(hdl)

如果你只是想编译和运行一个不同的程序,事情要简单得多:只需像往常一样编译生成的程序( gcc -Wall -g genfoo.cc -o genprog )并像往常一样执行它( systemfork + execve ,.. ..)

如果目标系统没有任何C编译器(但也考虑tinycc ,它能够从包含字符串的C代码 - 内存及其libtcc ),你不能这样做。 你需要使用一些JIT编译库,如libjitGNU lightningasmjit ....或者考虑嵌入一些脚本解释器,如LuaGuile

Windows详细信息

我不知道Windows,但传闻它具有等效的动态加载功能,例如LoadLibrary 如果要避免在运行时运行C编译器,则还需要一些JIT库。 细节不同( dllexport

一些框架或库( PocoQtBoost ....)正在为这些动态加载功能提供系统中立的包装器。


注意:在Linux上,你几乎可以做很多 dlopen (数十万),参见manydl.c 我的MELT系统(域专用语言来扩展GCC )正在产生C ++代码和dlopen -ing它的飞行。

另请注意,某些编程语言(如Common Lisp (以及Meta-Ocaml ))更适合运行时评估元编程多阶段 )。 值得注意的是,它的Common Lisp的SBCL实现能够编译任意表达式,然后在运行时运行它们! 另见J.Pitrat的博客

最简单的解决方案可能是在程序的文件中包含一个独立的编译器。 当程序需要重新编译时:

  1. 在系统上创建原始main.cpp源的另一个副本
    • 像: system("copy main.cpp new_main.cpp"); 在窗户上
  2. 打开new_main.cpp的流(假设您的代码名为new copy new_main.cpp
  3. 将附加代码附加到文件的结尾/开头
  4. 刷新并关闭输出流
  5. 调用编译器并将其传递给新文件的路径
    • 例如,如果你正在使用g ++,你会写一些类似的东西: g++ new_main.cpp -o new_main.exe
    • 如果您使用Microsoft Visual C ++编译器,则可以通过运行system("vcvarsall");来访问它system("vcvarsall"); (在C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC找到),后跟system("cl 'path/to/project/new_main.cpp'); (在C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\bin for me)。

在此之后你有new_main.exe ,这是main.cpp的编译形式+一些额外的代码。

警告:如果您分发应用程序,此选项将要求您安装编译器或包含其必需的文件。 您不需要使用new_main.exe安装/包含它们,但是您需要使用编译new_main.exe的程序安装/包含它们。


如果要从内存编译,过程类似:

  1. 打开一个到main.cpp的流
  2. main.cpp读取到char缓冲区(假设main.cpp只包含ASCII字符)
  3. 关闭到main.cpp的流
  4. 将代码附加到缓冲区的开头/结尾( 确保缓冲区中有空间!
  5. 调用编译器库
    • 此调用依赖于您的编译器库,但类似于: compileCode(String_Version_Of_Code_Read_From_File);

作为额外的信息,如果您需要动态修改和构建程序,通常会有更好的解决方案。 情况并非总是如此,但通常有更好的方法。

第三种选择可能是编写自己的编译器,但我会高度建议不要这样做。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM