c++ - 编写库时是否应将可见性/导出宏应用于模板?

标签 c++ dll shared-libraries visibility

在构建 C++ DLL 或共享库时,__attribute__((__visibility__("default")))__declspec(dllexport) 经常通过宏附加到那些具体的应该对库的使用者可用的符号(类、函数等),其他符号默认具有内部可见性。

但是对于内联函数或模板应该怎么办呢?

似乎对于内联函数,答案应该是不需要注解。如果定义内联函数的 header 的使用者实际上内联了该函数,则不需要符号。如果消费者改为发出外线定义,那仍然可以。唯一的问题是 DLL 内部和每个使用库内部的内联函数的定义可能不同。因此,如果您希望可靠地比较内联函数的地址,您可能会遇到一些麻烦,但这看起来很粗略。

鉴于该论点,似乎由于模板主体通常对消费 TU 完全可见,因此适用相同的逻辑。

我觉得这里可能有一些关于“外部模板”和显式实例化的微妙之处。

是否有人对可见性属性应如何遵循内联函数和模板有具体指导?

最佳答案

内联函数外部不可见(没有链接,IIRC),因此它们不能从 DLL 导出。如果它们是公开的,那么它们将被完整地写在你的库的头文件中,每个用户都重新编译它。

正如您在问题中所说,由于内联代码在使用该库的每个模块中都被重新编译,因此对于该库的 future 版本可能会出现问题。

我对共享库中的内联函数的建议是,它们应该只用于真正琐碎的任务或绝对通用的函数。请注意,将公共(public)内联函数转换为非内联函数是一项 ABI 重大更改。

例如:

  • A memcpy -样的功能。内联!
  • A bswap -样的功能。内联!
  • 类构造器。不要内联!即使它现在什么都不做,您可能希望在未来版本的库中做一些事情。在库中编写一个非内联的空构造函数并导出它。
  • 类析构函数。不要内联!同上。

实际上,一个内联函数可以有多个不同的地址这一事实并不重要。

关于extern templateexplicit instantiations,稍加小心,它们可用于从库中导出模板。如果模板实例化仅限于一组特定的情况,您甚至可以避免将模板代码复制到头文件中。

注意 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
}

关于c++ - 编写库时是否应将可见性/导出宏应用于模板?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24592302/

相关文章:

c++ - 防止链接器删除导出的函数

go - 如何以惯用方式构建现有包的扩展

c++ - 创建仅接受引用或指针的可变参数

c++ - 什么时候会使用未命名的共享内存?

c++ - 使用 visual studio 2013 构建 glut dll 时出错

c++ - 如何使用 gfortran 使用我的 .dll 构建 .lib 文件?

c++ - 覆盖 find_package 的默认搜索顺序

c++ - 设计 QFrame 和 QSizeGrip

Java (JNA) - 在 DLL (C++) 库中找不到函数

Unix 和 Windows 中的 Dll 卸载顺序