在讨论库的过程中,很多问题都涉及 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