- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在开发一个由许多插件组成的 C++ 库,这些插件可以相互独立地包含在内。插件集仅取决于用户在编译时的要求。这些插件只是源代码,它们不是独立的二进制文件。为此,主要的(也是唯一的)库的 CMakeLists.txt
有一个预定义的插件列表,并且在 plugins
目录中找到的每个插件都被添加到二进制目标.此外,还设置了一个带有插件名称的预处理器 #define
:
set (plugins
plugin1
plugin2
plugin3
...)
#optional plugins
foreach(library ${plugins})
file(TO_CMAKE_PATH "plugins/${library}" librarypath)
get_filename_component(librarypath ${librarypath} ABSOLUTE)
if(EXISTS ${librarypath})
message("--> found ${library}")
include_directories(${librarypath}/include)
file(GLOB libsources "${librarypath}/src/*.cpp" "${librarypath}/src/*.f90")
set(sources ${sources} ${libsources})
string(TOUPPER ${library} LIBRARY)
add_definitions(-D${LIBRARY})
endif()
endforeach(library)
现在在我的主库中,我基本上做的是:
#ifdef PLUGIN1
# include "plugin1.h"
#endif
#ifdef PLUGIN2
# include "plugin2.h"
#endif
#ifdef PLUGIN3
# include "plugin3.h"
#endif
...
// each plugin has a unique id:
enum PluginID : int {
Plugin1 = 1,
Plugin2 = 2,
Plugin3 = 3,
};
// the name of each plugin is associated with its ID,
PluginID getPluginIDFromName( const std::string& PluginName )
{
static std::map<std::string, PluginID> PluginIDMap = {
{"PLUGIN1", Plugin1},
{"PLUGIN2", Plugin2},
{"PLUGIN3", Plugin3},
};
return PluginIDMap[PluginName];
}
// Load a plugin by its ID
PluginBaseClass* pluginFactory( PluginID pluginID)
{
switch ( pluginID ) {
#ifdef PLUGIN1
case Plugin1: { return new class Plugin1();}
#endif
#ifdef PLUGIN2
case Plugin2: { return new class Plugin2();}
#endif
#ifdef PLUGIN3
case Plugin3: { return new class Plugin3();}
#endif
}}
所以结果是在主源中我可以通过以下方式加载插件:
PluginBaseClass* thePlugin1 = pluginFactory ( getPluginIDFromName ("PLUGIN1") );
一切都按预期工作,但我觉得我所做的是某种滥用 cmake 和预处理器宏。有没有更好的方法来实现我的目标?此外,为每个可能的插件手动更新 map
和 switch
相当麻烦。我的要求是用户不需要手动修改 CMakeLists.txt
。提前致谢!
编辑:我想通过它们的 ID 或它们的名称使插件可用,因此有两个功能。另外,首选静态链接;我认为没有理由进行动态加载。
最佳答案
您可以使用所谓的“自注册”并让编译器为您完成大部分工作,而不是手动创建从 id 到插件名称和工厂函数的映射。
首先,我们需要一个工厂类,各个插件可以在其中进行 self 注册。声明可能看起来像这样:
class PluginFactory
{
public:
using PluginCreationFunctionT = PluginBaseClass(*)();
PluginFactory() = delete;
static bool Register(std::string_view plugin name,
PluginID id,
PluginCreationFunctionT creation_function);
static PluginBaseClass* Create(std::string_view name);
static PluginBaseClass* Create(PluginID id);
private:
static std::map<std::string_view, PluginCreationFunctionT> s_CreationFunctionByName;
static std::map<PluginID, PluginCreationFunctionT> s_CreationFunctionById;
};
对应的源文件则包含
std::map<std::string_view, PluginFactory::PluginCreationFunctionT>
PluginFactory::s_CreationFunctionByName;
std::map<PluginID, PluginFactory::PluginCreationFunctionT>
PluginFactory::s_CreationFunctionById;
bool PluginFactory::Register(std::string_view const plugin name,
PluginId const id,
PluginCreationFunctionT const creation_function)
{
// assert that no two plugins accidentally try to register
// with the same name or id
assert(s_CreationFunctionByName.find(name) == s_CreationFunctionByName.end());
assert(s_CreationFunctionById.find(id) == s_CreationFunctionById.end());
s_CreateFunctionByName.insert(name, creation_function);
s_CreateFunctionById.insert(id, creation_function);
return true;
}
PluginBaseClass* PluginFactory::Create(std::string_view const name)
{
auto const it = s_CreationFunctionByName.find(name);
return it != s_CreationFunctionByName.end() ? it->second() : nullptr;
}
PluginBaseClass* PluginFactory::Create(std::string_view const id)
{
auto const it = s_CreationFunctionById.find(name);
return it != s_CreationFunctionById.end() ? it->second() : nullptr;
}
请注意,Register
始终返回 true
- 我们需要它返回一个值,以便将 Register
函数用作初始值设定项对于一个全局变量。作为全局变量的初始值设定项会导致编译器在程序启动期间发出代码以调用 Register
函数。
在您的主要功能中,您现在可以通过
获取特定插件的实例PluginBaseClass* thePlugin1 = PluginFactory::Create("PLUGIN1");
或通过
PluginBaseClass* thePlugin1 = PluginFactory::Create(PluginID::Plugin1);
现在需要修改插件本身,以便它们自行注册。理论上任何全局变量都可以,但是为了避免不同插件之间的名称冲突,最简单的方法是向每个插件类添加一个静态数据成员,例如
class Plugin1 : public PluginBaseClass {
public:
...
private:
static bool s_IsRegistered;
};
然后将以下内容添加到插件的源文件中:
namespace {
PluginBaseClass* create_plugin1()
{
return new Plugin1{};
}
}
bool Plugin1::s_IsRegistered
= PluginFactory::Register("PLUGIN1", PluginID::Plugin1, create_plugin1);
既然编译器生成了映射,您就不再需要预处理器定义了。您的 CMake 代码现在需要做的就是添加正确的包含目录和源代码。但这不需要成为主 CMake 文件的一部分。相反,您可以将 CMakeLists.txt 放入每个插件文件夹,然后通过 add_subdirectory
或 include
将它们包含到主 CMakefile 中:
foreach(library ${plugins})
if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/plugins/${library}/CMakeLists.txt)
message(STATUS "--> found ${library}"
include(${CMAKE_CURRENT_LIST_DIR}/plugins/${library}/CMakeLists.txt)
else()
message(FATAL "Unknown plugin ${library} requested!")
endif()
endforeach()
plugins/plugin1
文件夹中 plugin1 的 CMakeLists.txt
只包含
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
file(GLOB sources_plugin1 "${CMAKE_CURRENT_LIST_DIR}/src/*.cpp" "${CMAKE_CURRENT_LIST_DIR}/src/*.f90")
list(APPEND sources ${sources_plugin1})
在这种特殊情况下看起来可能没有太大改进,但现在拥有这些单独的 CMakeLists.txt
文件也允许有条件地添加依赖项。
例如,假设 Plugin2 是唯一使用 boost 的插件。使用单独的 CMakeLists.txt
,您可以将查找和使用 boost 所需的所有内容添加到 Plugin2 的 CMakeLists.txt
而不会污染主 CMakeLists.txt
文件。
关于C++/CMake : Making a plugin system for many 'source plugins' ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58580945/
我并不总是编写 make 文件,但当我这样做时,我喜欢尝试并写好它们。试图使界面与其他开发人员的期望一致始终是一项艰巨的任务。我正在寻找的是所有常见的 make some clean (GNU) ma
例如,我在运行 makefile 时收到这样的错误: make[3]: *** [/home/ziga/Downloads/openwrt/rakun_openwrt/staging_dir/ho
我必须创建一个 Makefile,它从不同文件夹中的 .c 文件创建单个可执行文件。 .c 文件包含来自任何这些文件夹的头文件。根目录有3个子目录x/y、x/z、a,所有这些子目录都有一些.c和.h文
您好,我有一个简单的 MakeFile,其中包含: clean: rm -f ex1 但是当我运行命令make clean 时,出现以下错误: make: *** No rule to mak
我已经为一些软件安装了它,但现在我根本不使用那个软件,所以我需要移除MinGW才能使用Cygwin进行Android开发。。我使用的是64位Windows 7
以下是针对我遇到的问题的简化生成文件: all: /tmp/makey/../filey @echo All done /tmp/filey: @echo Filey 当我运行 mak
获取错误: make: *** No rule to make target all. Stop." 在安装nagios主机期间运行此命令make all 最佳答案 可能的常见错误: 确保将文件命名为
当使用 -jN 运行 gnu-make 规则时,make 会创建 jobserver用于管理跨子制造商的工作数量。此外,您可以通过在 make 配方前添加 + 前缀来“将作业服务器环境传递”到 mak
使用 GNU Make 4.1 概括 我调用一个子品牌 b.mk来自生成文件 a.mk .b.mk被调用以确保构建子系统。 有时我想强制一个目标为 a.mk重制: make -f a.mk --al
这个问题与问题 2543127 的精神相似。 . 我有一个带有头文件列表的 gnu makefile。每个头文件可能位于不同的目录中,例如, HEADERS = $(wildcard *.h) $(w
假设我有以下 GNU make 目标: create_dir: @mkdir objects build_asm: $(ASM_FILES) @echo
我有一个具有以下结构的 Makefile(工作示例)。 .PHONY: image flashcard put-files put-files: @echo "=== put-files" i
我想要一个这样的makefile: cudaLib : # Create shared library with nvcc ocelotLib : # Create shared li
有没有比更好的方法来获取 GNU make 变量的第一个字符 FIRST=$(shell echo $(VARIABLE) | head -c 1) (不仅笨重而且还要调用外部shell)? 最佳答案
我通常使用像 cmake 这样的高级构建系统来构建我的 C/C++ 代码。但是由于各种原因,我直接使用 GNU make。 我正在进行递归构建,其中每个目录都有一个 makefile。 我最近不得不将
我通常使用像 cmake 这样的高级构建系统来构建我的 C/C++ 代码。但是由于各种原因,我直接使用 GNU make。 我正在进行递归构建,其中每个目录都有一个 makefile。 我最近不得不将
我安装了最新的mingw,发现没有mingw32-make了。有make.exe,所以我想知道最近是否将mingw32-make重命名为make.exe。 最佳答案 我不知道您从哪里获得 MinGW,
我正在使用 CentOS,但由于一个错误,许多软件包被删除了。所以我没有 yum 和 rpm。所以我想从源代码手动制作 yum,但我也没有制作。我知道一切都会用“制作包”制作。但是 make 自己呢?
考虑这个Makefile: .PHONY: all all: main.txt main.txt: build/main.txt cp build/main.txt . %/main.txt:
假设目录输入中有 1000 个扩展名为 .xhtml 的文件,并且这些文件的某个子集(输出路径在 $(FILES) 中)需要通过 xslt 转换为目录输出中具有相同名称的文件.一个简单的 make 规
我是一名优秀的程序员,十分优秀!