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

acm*_*acm 7 c++ dll visibility shared-libraries

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

但是应该如何处理内联函数或模板?

似乎对于内联函数,答案应该是不需要注释.如果定义内联函数的标题的使用者实际内联函数,则不需要符号.如果消费者发出了一个外线定义,那仍然可以.唯一的缺点是DLL内部和每个消费库内部的内联函数的定义可能不同.所以,如果你期望可靠地比较内联函数的地址,你可能会遇到麻烦,但无论如何这看起来都很粗略.

鉴于该论点,似乎因为模板主体通常对于消费TU完全可见,所以应用相同的逻辑.

我觉得这里可能存在一些关于'extern模板'和显式实例化的细微之处.

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

rod*_*igo 5

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

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

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

例如:

  • 类似函数memcpy。排队!
  • 类似bswap函数。排队!
  • 一个类构造函数。不要内联!即使它现在什么也不做,您可能想在库的未来版本中做一些事情。在库中编写一个非内联空构造函数并将其导出。
  • 类析构函数。不要内联!与上面相同。

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

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

注意 1:在下面的示例中,我将使用一个简单的函数模板,但类模板的工作方式完全相同。注 2:我使用的是 GCC 语法。MSC 代码是相似的,我想你已经知道差异了(而且我没有 MSC 编译器来测试)。

实施例1

公共_foo.h

template<int N> int foo(int x); //no-instantiable template
Run Code Online (Sandbox Code Playgroud)

共享_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);
Run Code Online (Sandbox Code Playgroud)

程序.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
}
Run Code Online (Sandbox Code Playgroud)

相反,如果您的模板应该可以自由实例化,但您希望它以特定方式频繁使用,则可以从库中导出这些模板。想一想std::basic_string:它最有可能被用作std::basic_string<char>and std::basic_string<wchar_t>,但不太可能被用作std::basic_string<float>

实施例2

公共_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);
Run Code Online (Sandbox Code Playgroud)

共享_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);
Run Code Online (Sandbox Code Playgroud)

程序.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
}
Run Code Online (Sandbox Code Playgroud)