如何在运行时使用 C++ 在工厂类中动态注册类
阅读有关 C++ 的更多信息,至少是一本好的C++ 编程书籍,并查看一个好的 C++ 参考网站,以及后来的n3337,即 C++11 标准。还请阅读您的 C++ 编译器(可能是GCC或Clang)的文档,如果您有,请阅读您的操作系统的文档。如果您的操作系统中可以使用插件,您可以在运行时注册一个工厂函数(通过在加载提供它的插件之后引用该函数)。例如,Mozilla firefox浏览器或最近的GCC编译器(例如GCC 10启用插件)或鱼壳正在执行此操作。
所以我想知道我是否可以在运行时从配置文件(见下文)动态注册类。
大多数 C++ 程序都在操作系统下运行,例如 Linux。一些操作系统提供插件机制。对于 Linux,请参阅dlopen(3)、dlsym(3)、dlclose(3)、dladdr(3)和C++ dlopen mini-howto。对于 Windows,请深入了解其文档。
因此,使用最近的 C++ 实现和一些最近的操作系统,您可以在运行时注册工厂类(使用插件),并且可以找到库(例如Qt或POCO)来帮助您。
但是,在纯标准 C++ 中,翻译单元集是静态已知的,并且不存在插件。因此,给定程序中的函数、lambda 表达式或类的集合是有限的,并且不会随时间变化。
在纯 C++ 中,有效函数指针集或给定 变量的有效可能值集std::function是有限的。其他任何事情都是未定义的行为。在实践中,许多现实生活中的 C++ 程序通过其操作系统或 JIT 编译库接受插件。
您当然可以考虑使用JIT 编译库,例如asmjit或libgccjit或LLVM。它们是特定于实现的,因此您的代码将不可移植。
在 Linux 上,许多Qt或GTKmm应用程序(例如KDE和大多数 Web 浏览器,例如Konqueror、Chrome或 Firefox)都是用 C++ 编码的,并使用工厂函数加载插件。检查strace(1)和ltrace(1)。
传闻微软的Trident Web 浏览器是用 C++ 编码的,并且可能接受插件。
我试过使用宏,但宏中的参数不能是字符串。
宏参数可以被字符串化。你可以玩x-macros技巧。
我想要的只是在我想注册另一个类时避免重新编译。
在 Ubuntu 上,我建议在您的程序或库中接受插件
将dlopen(3)与绝对文件路径一起使用;该插件通常会作为程序选项(如RefPerSys或GCC 那样)传递,并dlopen在程序或库初始化时 -ed。实际上,您可以拥有很多插件(数十万个,请参阅manydl.c并使用pmap(1)或proc(5)进行检查)。插件中的dlsym(3) -ed C++ 函数应声明extern "C" 为禁用名称修改。
单个 C++ 文件插件 (in yourplugin.cc) 可以使用 g++ -Wall -O -g -fPIC -shared yourplugin.cc -o yourplugin.so和以后编译dlopen "./yourplugin.so"或绝对路径 (或适当地配置您的$LD_LIBRARY_PATH-see ld.so(8) - 并传递"yourplugin.so"给dlopen)。还要注意Rpath。
还可以考虑使用libgccjit (至少在将您的 GCC 升级到GCC 9之后,可能通过从其源代码编译它)(它比在某些文件中生成临时 C++ 代码并将该文件编译到临时插件中更快)。
为了便于调试加载的插件,您可能会对 Ian Taylor 的libbacktrace 感兴趣。
请注意,您的程序的全局符号(声明为extern "C")可以通过将nullptr 文件路径传递给dlopen(3)来按名称访问,然后在获得的句柄上使用dlsym(3) 。你想-rdynamic -ldl在链接你的程序(或你的共享库)时通过。
我想要的只是在我想注册另一个类时避免重新编译。
您可能会在不同的翻译单元中注册课程(大概是一个简短的翻译单元)。您可以从RefPerSys 文件的多个#include-s中获得灵感generated/rps-name.hh。然后,您只需重新编译单个*.cc文件并重新链接整个程序或库。请注意,Qt在它moc的 .
另请阅读 J.Pitrat 关于人工存在的书:有意识的机器的良心ISBN,它解释了为什么元编程方法很有用。研究GCC(或RefPerSys)的源代码,在相关时使用或从SWIG、ANTLR、GNU bison(它们都生成 C++ 代码)中获取灵感