我正在编写一个小型库,该库在内部使用操作系统套接字API进行一些联网。
在Windows上,需要在开始时调用WSAStartup
,在结束时调用WSACleanup
来初始化Winsock。然而,文档中说,它可以多次初始化Winsock,然后清理相同次数的Winsock,因为它在内部使用引用计数。
现在我正在解决一个难题。对图书馆作家来说,什么是更好的做法?
- 提供调用
WSAStartup
/WSACleanup
的全局函数initializeLibrary
/terminateLibrary
,并指示用户在应用程序开始/结束时调用它们 - 在我的类的构造函数/析构函数中内部调用
WSAStartup
/WSACleanup
,根本不用麻烦用户
现在我看到第二个选项看起来更方便,但这是个好主意吗?这样做难道没有任何隐藏的不良后果吗?它能对性能产生影响吗?图书馆秘密这样做是个好做法吗?
库秘密执行此操作是一种好做法吗?
我可能错过了这里的要点,但对我来说,任何库的要点都是从最终用户那里抽象细节,使使用它变得轻松。当然,抽象可能会在一定程度上限制灵活性,但我觉得这里不是这样。请注意,我只是在讨论"保密"问题,老实说,我不知道它是如何影响性能的。
对于库作者来说,什么是更好的做法?
此处缺少第三个选项。我通常反对init()
和finalize()
函数,因为它反对RAII——用户可能会忘记调用它们。但是,您可以设计一个仅在应用程序的"根"处创建的"令牌"类,这是使用API的任何其他部分所必需的。考虑一下:
#include <WinSock2.h>
class ApiKey {
public:
ApiKey() {
auto wsadata = WSADATA();
auto startupResult = WSAStartup(MAKEWORD(2, 2), &wsadata);
// ...
}
~ApiKey() {
auto cleanupResult = WSACleanup();
// ...
}
};
class Socket {
public:
Socket(ApiKey& key) {
}
// ...
};
我不是boost::asio用户,但据我所知,他们就是这么做的。io_context
类充当ApiKey
。
如果你想了解更多信息,你可以在他们的源代码中查找:
https://github.com/boostorg/asio/blob/develop/include/boost/asio/detail/impl/winsock_init.ipp
https://github.com/boostorg/asio/blob/develop/include/boost/asio/io_context.hpp