在非模板库类中拥有静态成员的最佳方法是什么,而不必在类用户上承担定义成员的负担?
说我想提供这个课程:
class i_want_a_static_member
{
static expensive_resource static_resource_;
public:
void foo()
{
static_resource_.bar();
}
};
Run Code Online (Sandbox Code Playgroud)
那么类的用户一定不要忘了某处定义静态成员(如已经回答了 很多 次):
// this must be done somewhere in a translation unit
expensive_resource i_want_a_static_member::static_resource_;
Run Code Online (Sandbox Code Playgroud)
我在下面有一个答案,但它有一些缺点.是否有更好和/或更优雅的解决方案?
我有一个使用cmake命令设置的仅头文件库项目:
add_library(my_library INTERFACE)
Run Code Online (Sandbox Code Playgroud)
而且我还补充道
target_sources(my_library INTERFACE ${MY_LIRBARY_HEADER_FILES})
Run Code Online (Sandbox Code Playgroud)
但是当我打开一个源文件时,我收到警告:
此文件不属于任何项目目标,代码洞察功能可能无法正常工作
而且我在代码完成等方面失去了很多功能.
设置它的正确方法是什么,以便CLion在仅头文件库中提供其常用功能?
我试图用SO找到答案.有许多问题列出了在c ++中构建一个只有头文件库的各种优缺点,但是我无法找到一个以可量化的术语来构建这样的库.
因此,在可量化的术语中,使用传统上分离的c ++标头和实现文件与仅标头之间有什么不同?
为简单起见,我假设不使用模板(因为它们只需要标题).
详细说明,我列出了我从文章中看到的优点和缺点.显然,有些不容易量化(例如易用性),因此无法用于量化比较.我会用可量化的方式标记那些我期望可量化的指标.
仅限标题的优点
仅限标题的缺点
您可以从较大的开源项目(比较类似大小的代码库)中使用的任何示例都非常感激.或者,如果您知道可以在仅标题版本和分离版本之间切换的项目(使用包含两者的第三个文件),那将是理想的.轶事数字也很有用,因为它们给了我一个可以获得一些洞察力的球场.
利弊来源:
提前致谢...
更新:
对于可能稍后阅读并且有兴趣获得关于链接和编译的一些背景信息的任何人,我发现这些资源很有用:
更新:(回应下面的评论)
仅仅因为答案可能不同,并不意味着测量是无用的.你必须开始测量一些点.您拥有的测量值越多,图像就越清晰.我在这个问题上要求的不是整个故事,而是对图片的一瞥.当然,如果他们想要不道德地宣传他们的偏见,任何人都可以使用数字来扭曲争论.但是,如果有人对两个选项之间的差异感到好奇并发布这些结果,我认为这些信息很有用.
没有人对这个话题感到好奇,足以衡量它吗?
我喜欢枪战项目.我们可以从删除大部分变量开始.只在一个版本的linux上使用一个版本的gcc.仅对所有基准测试使用相同的硬件.不要使用多个线程进行编译.
然后,我们可以测量:
我想将我正在处理的库打包为仅限标头的库,以便客户端更容易使用.(它很小,并且没有理由将它放入单独的翻译单元中)但是,我不能简单地将我的代码放在标题中,因为这违反了C++的一个定义规则.(假设库头包含在客户端项目的多个翻译单元中)
如何修改库以使其成为仅标题?
我创建了几个目前只有标头的 C++库.我的类的接口和实现都写在同一个.hpp文件中.
我最近开始认为这种设计不是很好:
我真的很喜欢只有头文件库的方面:所有函数都可以内联,并且它们非常容易包含在你的项目中 - 不需要编译/链接任何东西,只需要一个简单的#include指令.
是否有可能充分利用两个世界?我的意思是 - 允许用户选择他/她想要如何使用库.它还可以加快开发速度,因为我以"动态链接模式"处理库以避免荒谬的编译时间,并以"仅标题模式"发布我的成品以最大化性能.
第一个逻辑步骤是将接口和实现划分为.hpp和.inl文件.
不过,我不确定如何前进.我已经看到很多库将LIBRARY_API宏添加到它们的函数/类声明中 - 可能需要类似的东西来允许用户选择?
我的所有库函数都以inline关键字为前缀,以避免"多个定义......"错误.我假设关键字将被文件中的LIBRARY_INLINE宏替换.inl?宏将解析inline为"仅标题模式",而不是"动态链接模式".
我正在编写一个C++库(仅限标题),并使用CMake生成我的(Visual Studio)项目和解决方案文件.我也在编写一个测试套件,它是同一个CMake项目的一部分.
当我在代表我的仅头文件库的目标上调用target_include_directories()时,会出现问题,因此我的库的使用者可能会找到其头文件.我收到以下错误消息(即使生成没有中止).
CMake Error in CMakeLists.txt:
Target "Fonts" INTERFACE_INCLUDE_DIRECTORIES property contains path:
"D:/Projects/GPC/fonts/include"
which is prefixed in the source directory.
Run Code Online (Sandbox Code Playgroud)
(D:/ Projects/GPC/Fonts是我的库项目的顶级目录.如果我将头文件移动到顶层目录,问题仍然存在.)
我的CMakeLists.txt中的违规行是这个(为简单起见):
target_include_directories(Fonts INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
Run Code Online (Sandbox Code Playgroud)
我不明白我做错了什么.没有target_include_directories(),消费者项目的代码根本不能包含我的头文件(除非在已安装的形式,但我还没有达到这一点,并且无论如何我希望能够从其构建树中使用我的库,没有安装.)
我觉得我在这里缺少一些基本的东西; 但是我没有找到解决方案或解释就搜索了几个小时.
是否有这样的依赖模式,不可能只保留头文件中的所有内容?如果我们只对每个标题强制执行一个类的规则怎么办?
出于这个问题的目的,让我们忽略静态的东西:)
我正在开发一个只包含头文件的库.到目前为止,它只包含类,这很好.但是,我已经到了这样的程度,我需要在库中有一些库中可访问的不可变数据(即,不是类实例数据)来实现某些功能.你显然不能只将全局数据放在头文件中,否则每个标题的编译单元#include都会有一个符号定义,你会在链接时得到多个定义错误.
我似乎找到了一个解决方法,让我可以在类中拥有静态数据,而无需通过将数据static作为函数中的变量并返回指向该数据的指针来向库中添加编译单元:
class StaticData {
public:
void doSomething() { /* this uses getData */ }
void doSomethingElse() { /* this does too */ }
private:
static int* getData() {
static int array[] { 1, 2, 3, 4 };
return array;
}
};
Run Code Online (Sandbox Code Playgroud)
这似乎工作正常,但我必须承认我不知道头文件static中的inline函数中的函数数据会发生什么.我想知道这个"黑客"是否有任何意想不到的影响,比如#include这个标题的每个编译单元都有自己的版本array.编译器如何以及在何处决定使用它?
还应该注意的是,我没有使用它来实现单例反模式或任何东西.我只是用它来存储多个函数需要使用的数据(这就是为什么它不能static只是一个使用它的函数,但即使它确实如此,也会提示相同的问题).