我正在为C库编写C ++包装器。我需要确保C库的初始化函数始终在任何其他函数之前执行。我想这样做,以便我的C ++包装器的用户不需要手动进行初始化。
AFAICS确保这种情况发生的一种方法是将库函数封装在单例类中,并在类的构造函数中执行初始化函数:
class Engine {
private:
static const Engine _instance ;
Engine() { c_library_initializer() ; }
// ... prevent copy construction and copy assignment
public:
static const Engine & handle () const { return _instance ; }
// ... non-static const wrappers for other c library functions
} ;
这种方法的一个缺点是它需要一些额外的编码:在库中用于防止复制和库的用户,因为所有库方法都必须通过实例句柄调用:
const Engine & lib = Engine::handle() ;
lib.this_function() ;
lib.that_function() ;
另一个迂腐的缺点是所有的包装函数都在静默地传递一个从未使用过的this指针。我意识到这对今天的内存/处理器来说并不是一个大问题,但它仍然是IIUC。也许优化编译器会查找这些未使用的变量并避免传递它们?我不知道。
我想到了一种更简单的方法,在库的CPP文件中使用内部类和单个实例,这对我的包装器的用户来说是不可见的(因此不需要复制保护或返回句柄)或通过该句柄调用):
struct EngineInit {
EngineInit() { c_library_initializer() ; }
} _EngineInit ;
然后,包装器函数只是通过H / HPP文件向我的包装器用户公开的普通(非成员)函数。
人们是否发现上述方法有任何问题和/或人们可以为我想要达到的目标提出其他更简单的方法吗?
答案 0 :(得分:2)
我想这样做,以便我的C ++包装器的用户不需要手动 做初始化。
这是一个坏主意。如果您的用户决定从静态变量的构造函数中使用您的库,该怎么办?如果你不经意间自己做了怎么办?要求用户初始化库是一种常见且明智的做法。
答案 1 :(得分:1)
如果库没有很多功能,另一种方法是将每个函数包装在另一个函数中,检查是否进行了初始化(使用静态变量)。你失去了一些时间进行检查,但你不需要打扰一个单身人士。
答案 2 :(得分:1)
您可以使用基类初始化一次库:
#include <iostream>
#include <mutex>
void c_library_initializer() {
std::cout << "c_library_initializer\n";
}
class Base
{
protected:
Base();
};
Base::Base() {
static std::once_flag once;
std::call_once(once, [] { c_library_initializer(); });
}
class Derived : public Base
{};
int main() {
Derived d0;
Derived d1;
}
如果没有C ++ 11,您可以使用boost :: call_once:http://www.boost.org/doc/libs/1_32_0/doc/html/call_once.html。
答案 3 :(得分:1)
我不知道它是否可以彻底解决您的问题,但在阅读您的问题时,我想到了一件事。
您可以将库包装到共享对象(.so
)中。这样您就可以使用__attribute__((constructor))
函数上的c_library_initializer()
。
构造函数属性导致在执行
main ()
之前自动调用该函数。
请注意,这是GCC extension。
答案 4 :(得分:0)
当然,简单的解决方案是静态构造函数吗?
在程序中声明类类型的非局部静态变量,并且将调用该类的构造函数以在很早的阶段初始化变量。您可以在构造函数中放置您喜欢的任何代码,例如初始化C库。
究竟什么时候调用构造函数取决于它是静态还是动态初始化,但它总是在同一个翻译单元中的任何其他代码执行之前。
或者我错过了什么?
我认为这可能是你在第二个例子中的方向,但你没有走得太远。我会写这样的代码。
// EngineWrapper.cpp
struct EngineInit {
EngineInit() { c_library_initializer(); }
};
EngineInit do_not_use; // this static definition triggers initialisation
// Wrapper functions. Note that only wrapper functions contained in this translation unit
// will guarantee to trigger initialisation.
struct EngineWrapper {
// wrapper 1
// wrapper 2
};