编写库时,是否应将可见性/导出宏应用于模板
Should visibility/export macros be applied to templates when writing a library?
在构建C++DLL或共享库时,__attribute__((__visibility__("default")))
或__declspec(dllexport)
经常通过宏附加到那些应该提供给库使用者的具体符号(类、函数等(上,其他符号默认为具有内部可见性。
但是,对于内联函数或模板应该做些什么呢?
似乎对于内联函数,答案应该是不需要注释。如果定义内联函数的头的使用者实际上内联了函数,那么就不需要符号了。如果使用者发出一个越界的定义,那还是可以的。唯一的问题是DLL内部和每个使用者库内部的内联函数的定义可能不同。因此,如果您希望可靠地比较内联函数的地址,您可能会遇到一些麻烦,但这似乎很粗略。
鉴于这一论点,由于模板主体通常对消费TU完全可见,因此同样的逻辑也适用。
我觉得这里可能有一些关于"外部模板"和显式实例化的微妙之处。
是否有人对可见性属性应如何遵守内联函数和模板有具体的指导?
内联函数在外部不可见(没有链接,IIRC(,因此无法从DLL导出。如果它们是公共的,那么它们会完全写入库的头文件中,每个用户都会重新编译它
正如您在问题中所说,由于内联代码在使用该库的每个模块中都会重新编译,因此对于该库的未来版本,可能会出现问题。
我对共享库中的内联函数的建议是,它们应该只用于真正琐碎的任务或绝对通用的函数。请注意,将公共内联函数转换为非内联函数是破坏ABI的更改。
例如:
- 一个类
memcpy
函数。内联 - 一个类似
bswap
的函数。内联 - 类构造函数。不要内联!即使它现在什么都不做,你也可能想在未来版本的库中做点什么。在库中编写一个非内联的空构造函数并将其导出
- 类析构函数。不要内联!同上
实际上,一个内联函数可以有几个不同的地址,这一点并不重要。
关于外部模板和显式实例化,请注意,它们可以用于从库导出模板。如果模板实例化仅限于一组特定的情况,您甚至可以避免将模板代码复制到头文件中。
注1:在下面的例子中,我将使用一个简单的函数模板,但类模板的工作原理完全相同。注2:我使用的是GCC语法。MSC代码是相似的,我想你已经知道区别了(我没有MSC编译器来测试(。
示例1
public_foo.h
template<int N> int foo(int x); //no-instantiable template
shared_foo.cpp
#include "public_foo.h"
//Instantiate and export
template __attribute__ ((visibility("default")))
int foo<1>(int x);
template __attribute__ ((visibility("default")))
int foo<2>(int x);
程序.cpp
#include "public_foo.h"
int main()
{
foo<1>(42); //ok!
foo<2>(42); //ok!
foo<3>(42); //Linker error! this is not exported and not instantiable
}
相反,如果您的模板应该是可自由实例化的,但您希望它以特定的方式频繁使用,则可以从库中导出这些模板。想象一下std::basic_string
:它最有可能被用作std::basic_string<char>
和std::basic_string<wchar_t>
,但不太可能用作std::basic_string<float>
。
示例2
public_foo.h
template<int N> int foo(int x)
{
return N*x;
}
//Do not instantiate these ones: they are exported from the library
extern template int foo<1>(int x);
extern template int foo<2>(int x);
shared_foo.cpp
#include "public_foo.h"
//Instantiate and export
template __attribute__ ((visibility("default")))
int foo<1>(int x);
template __attribute__ ((visibility("default")))
int foo<2>(int x);
程序.cpp
#include "public_foo.h"
int main()
{
foo<1>(42); //ok, from library
foo<2>(42); //ok, from library
foo<3>(42); //ok, just instantiated
}
我认为您要问的问题可以归结为以下两个问题:
何时需要显式导出带有__attribute__((__visibility__("default")))
的符号
这里的规则是,如果方法的实现是共享库内部的(通常,它在.cpp文件中,在external.h中有一个声明(,则在编译共享库时,需要将该符号的声明标记为外部可见。如果您未能做到这一点,那么在外部库中编译任何使用该方法的东西都会在链接时抱怨——所以这个问题很容易在测试中发现。
这是假设您还添加了-fvisibility=hidden
来默认内化所有符号。
将__attribute__((__visibility__("default")))
添加到具有外部可见定义的内联函数或模板函数中有害吗
事实并非如此,因为默认可见性的一个常见用例是标记整个类以进行导出,这可能包括outline和inline方法。在这种情况下,内联方法将是内联的(在任何使用它的翻译单元中(,并且不生成外部符号。如果您稍后选择勾勒该方法的轮廓,则符号可见性将生效。
对于模板,显式模板实例化只是一种允许您为模板方法创建大纲定义的机制,并且与普通内联/大纲方法应用相同的规则。
- c++r值引用应用于函数指针
- 如何更改唯一指针向量的可见性
- 当指向对象的指针作为参数传递给 std::thread 时,内存可见性
- 如何将记忆应用于此递归函数?
- atoi() 在应用于大型命令行参数时会产生不正确的值
- 限制静态库中符号的可见性 (MSVC/Visual C++)
- 运算符'<'不能应用于类型 'string' 和 'string' 的操作数
- 应用于运算符而不是构造函数的显式关键字
- 我们可以将阈值应用于色彩空间模型的单个组件(如 RGB 和 LAB)吗?
- 应用于无符号类型的一元减号运算符
- 班级成员可见性C++
- C++函数,它将数组、谓词和运算符作为参数,并将运算符应用于满足谓词的数组元素
- 我可以将c ++清理器仅应用于程序的一部分而不是第三方库吗?
- 内联模板函数的可见性
- 您可以将binary_search应用于具有自定义类型的矢量吗?
- 如何在OpenGL中将纹理应用于子网格
- std::mutex 的发布-获取可见性保证是否仅适用于关键部分?
- 考虑 .(点)应用于应该是运行时多态性的东西时
- 如何使用多态性将相同的操作应用于具有相同基类的不同对象的unordered_sets
- 编写库时,是否应将可见性/导出宏应用于模板