在讨论该库的过程中,很多人询问了关于 ABI 稳定性的问题,以及库是否应该关注它。我们认为使 ABI 稳定可能是一个有用的功能,但这会增加大量的开销,并降低库的易用性。对于那些不需要跨编译器 ABI 稳定性的用户来说,这个功能将是多余的。
我们决定让这个库更加简单和底层,以便能够用于为需要它的用户构建 ABI 稳定的插件系统,同时又不为其他用户增加开销。
有一些现有的 C++ 插件系统。它们中的大多数都强制用户使用一些预定义的 API。问题在于,所有这些 API 都不同。
为了更易用,Boost.DLL 不强制 API。由用户自己设计合适的 API。
该库的一些方法使用了 boost::filesystem::path 或返回 std::vector<std::string>。这乍一看可能不那么优化,但这样做是有原因的。
boost::filesystem::path 可以透明地使用 Unicode 字符串和非 Unicode 字符串。使用它为库提供了更友好的用户界面,而性能开销在 boost::filesystem::path 接受方法的文件系统操作相对较慢,因此不会被注意到。
std::vector<std::string> 变量由 library_info 方法返回。查询库本身就是一个缓慢的过程:它会随机读取文件的一部分并执行算法,这些算法有时会根据文件部分或导出符号的数量具有线性复杂度。返回 std::vector<std::string> 可以简化实现,并且不需要用户在查询后保留 library_info 的实例。在很少调用的方法中,性能开销不是很显著,这似乎是合理的。
其他方法被认为是热路径,并尽可能进行了优化。
通过 shared_library(program_location()) 进行自加载而不是使用 shared_library::load_self() 成员方法,有一个很好的理由。这个理由是需要能够从任何地方(即使是主二进制文件)调用 shared_library(this_line_location())。我们需要这个来链接插件到二进制文件并创建一个透明的引用计数机制。
我认为创建做完全相同事情的多个接口是不合理的,这就是为什么 shared_library(program_location()) 和 shared_library(this_line_location()) 在没有 shared_library::load_self() 的情况下被使用。
名字修饰取决于源代码,例如 "boost::foo" 可能是 foo 函数或 foo 变量。取决于这些知识,它必须以不同的方式进行名字修饰。如果 foo 是一个重载函数,接受参数:"boost::foo(variant<int, short>)",那么问题就更多了。在这种情况下,必须指定参数的完整名称,这可能是 boost::variant<int, short> 或 variant<int, short, void_, void_> ...
有一个想法是允许用户前向声明函数并从它生成名字修饰后的名称。
namespace boost { void foo(variant<int, short>); } std::string mangled_name = boost::dll::magic_mangle(boost::foo);
但这个想法因为链接器问题而彻底失败,并且在编译时没有可靠的方法从编译器内部获取名字修饰后的符号名称。
这就是为什么别名被认为是“两害相权取其轻”。
BOOST_DLL_ALIAS(boost::foo, foo_variant) // in plugin "foo_variant" // in plugin importer