那么,我想做什么:基本上我想从另一个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下使用。
一个例子非常有用。
答案 0 :(得分:2)
特定于操作系统。如果第二个(构建的)程序是独立程序或库(或第一个加载的代码),则情况会有所不同。
在Linux(和其他Posix系统)上,您可以生成C或C ++文件(通过使用常用的文本输出函数;我建议在内存中构建生成的C ++文件的一些AST)。然后你可以通过分支进程来运行一些C ++编译器(在简单的情况下,使用popen(3)或system(3),或使用较低级fork(2)& execve(2),等等....)。有关详情,请参阅Advanced Linux Programming。
然后,您可以使用dlopen(3)动态加载获取的共享对象,并使用dlsym
使用-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"
的主程序访问。
将编译分成具有位置无关代码的共享对象(shared library):因此从程序内部运行:g++ -Wall -fPIC -g -O -shared genfoo.cc -o genfoo.so
从您的主程序中调用dlopen
和dlsym
(请注意,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
)并像往常一样执行它({{1} },system
+ fork
,....)
如果目标系统没有任何C编译器(但也考虑tinycc能够从包含字符串的C代码在内存中编译execve
),则不能这样做。您需要使用JIT compilation,libjit,GNU lightning之类的asmjit库。 ....或者考虑嵌入一些脚本解释器,如Lua或Guile
我不了解Windows,但传闻它具有相同的dynamic loading功能,例如LoadLibrary。如果要避免在运行时运行C编译器,您还需要一些JIT库。细节不同(libtcc
)
某些框架或库(Poco,Qt,Boost ....)正在为这些动态加载功能提供系统中立的包装。
注意:在Linux上,您几乎可以执行dllexport
(数十万)的批次,请参阅manydl.c。我的MELT系统(扩展GCC的特定于域的语言)正在生成C ++代码并且dlopen
即时生成它。
另请注意,某些编程语言(如Common Lisp(以及Meta-Ocaml))更适合运行时evaluation&amp; meta-programming(multi-staged)。值得注意的是,Common Lisp的SBCL实现能够编译任意表达式,然后在运行时运行它们!另请参阅J.Pitrat's blog。
答案 1 :(得分:1)
最简单的解决方案可能是在程序的文件中包含一个独立的编译器。当程序需要重新编译时:
system("copy main.cpp new_main.cpp");
new_main.cpp
的流(假设您的代码名为新副本new_main.cpp
)g++ new_main.cpp -o new_main.exe
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
中找到)来访问它我)。在此之后你有new_main.exe
,这是main.cpp
的编译形式+一些额外的代码。
警告:如果您分发应用程序,此选项将要求您安装编译器或包含其必需的文件。您不需要使用new_main.exe
安装/包含它们,但是您需要使用编译new_main.exe
的程序安装/包含它们。
如果要从内存中编译,过程类似:
main.cpp
main.cpp
读取到char缓冲区(假设main.cpp
仅包含ASCII字符)main.cpp
compileCode(String_Version_Of_Code_Read_From_File);
作为额外的信息,如果您处于需要动态修改和构建程序的情况,通常会有更好的解决方案。情况并非总是如此,但通常会有更好的方法。
第三种选择可能是编写自己的编译器,但我强烈反对。