我是一个真正的 C++ 菜鸟,所以请耐心等待。首先让我们设置舞台。
我有一个 C++ 源代码,binary.cpp
它编译成一个二进制文件,如下所示:
# include "lotsofheaders.h"
int main(int argc, char* argv[])
{
int errorcode = FOOBAR_GLOBAL_UNKNOWN;
// foobar instanciation
Foobar foobar();
// multiple calls to :send_auth passing in foobar instance
errorcode = send_auth(getX(), getY(), foobar);
errorcode = send_auth(getX(), getY(), foobar);
errorcode = send_auth(getX(), getY(), foobar);
return errorcode == FOOBAR_OK ? EXIT_SUCCESS : EXIT_FAILURE;
}
该send_auth
方法是从另一个目标代码文件加载的,并传递了一个 foobar 的实例。原因是,Foobar 来自一个我没有来源的 API 对象,并且不能多次实例化。
由于 main 只被调用一次,所以一切都按预期工作:只有一个 Foobar 实例,send_auth
可以多次调用。
二进制文件对我没有任何用处,我需要一个共享对象库来做同样的事情。它只创建一个 Foobar 实例并公开一个外部接口方法send_with_auth
,在我的共享对象库加载后可以多次调用该方法。
我的库代码library.cpp
看起来像这样:
# include "lotsofheaders.h"
# include "library.h"
const char* send_with_auth(const char* X, const char* Y){
std::string x(X);
std::string y(Y);
int result = send_auth(x, y, 'some Foobar singleton');
return true;
}
由于我通过 Ruby FFI 加载我的共享对象库,因此我的库需要一些 C 样式的标头library.h
:
extern "C" {
const char* send_with_auth(const char* X, const char* Y);
}
现在有了舞台设置,我必须在我的库中只创建一次 Foobar 的实例,并将其传递给每次调用,send_auth
以免 Foobar 出现内存冲突错误。
据我了解,这是我对单例的(过于复杂)尝试。有一个新的library.h
像这样:
extern "C" {
class Singleton
{
private:
static bool instanceFlag;
static Singleton *single;
Singleton()
{
//private constructor
}
public:
static Foobar* fo;
static Singleton* getInstance();
~Singleton()
{
instanceFlag = false;
}
};
const char* send_with_auth(const char* X, const char* Y);
}
并且有这个实现library.cpp
:
# include "lotsofheaders.h"
# include "library.h"
bool Singleton::instanceFlag = false;
Singleton* Singleton::single = NULL;
Singleton* Singleton::getInstance()
{
if(! instanceFlag)
{
single = new Singleton();
instanceFlag = true;
// bringing up my Foobar instance once and only once
Foobar fo;
single->fo = &fo;
return single;
}
else
{
return single;
}
}
const char* send_with_auth(const char* X, const char* Y){
std::string x(X);
std::string y(Y);
Singleton *single;
single = Singleton::getInstance();
int result = send_auth(x, y, *single->fo);
return true;
}
这段代码至少可以编译,我可以将所有内容绑定到一个共享对象库。现在,当我在外部进程(在我的情况下使用 Ruby FFI 的 Ruby 模块)中加载该库时,我总是会收到错误消息:
Could not open library '/some/path/libfoobar.so': /some/path/libfoobar.so: undefined symbol: _ZN9Singleton2erE (LoadError)
我很确定我从 library.o 到 libfoobar.so 的编译/绑定/剥离过程是可以的,因为它在其他情况下会成功。我很确定我在这里误解了 C++ 的单例概念。我想知道如何实现我的目标:在我的共享对象库中只创建一个 Foobar 实例,并将它传递给我的库向外部公开的唯一方法的每次调用。
有人可以帮忙吗?问候菲利克斯
更新
在库中使用 CommandlineParser 是愚蠢的。事实上,它只是返回了两个 C 字符串。API 库路径和日志目录。有了它,我重新创建了一个命名空间:
namespace
{
char* home("/path/to/api/libs");
char* log("/tmp");
Foobar foobar(home, log);
}
这会导致我加载库时出现段错误。与此相反,我可以将这些行直接放入我的函数中:
const char* send_with_auth(const char* X, const char* Y){
std::string x(X);
std::string y(Y);
char* home("/path/to/api/libs");
char* log("/tmp");
Foobar foobar(home, log);
int result = send_auth(x, y, &foobar);
return true;
}
这里一切正常,除了第二次调用send_with_auth
let 让一切崩溃导致 Foobar 再次实例化。
更新 2:
最后我更简单地解决了它,使用全局可用的 bool 开关来初始化我的 Foobar 实例一次:
namespace baz{
bool foobarInitialized = false;
}
const char* send_with_auth(const char* certificatePath, const char* certificatePin, const char* xmlData){
std::string certificate_path(certificatePath);
std::string certificate_pin(certificatePin);
std::string xml_data(xmlData);
Foobar *foobar_ptr;
if (! baz::foobarInitialized) {
char* home_dir("/path/to/api/lib");
char* log_dir("/tmp");
foobar_ptr = new Foobar(home_dir, log_dir);
baz::foobarInitialized = true;
}
int result = send_auth(x, y, foobar_ptr);
return xml_data.c_str();
}
现在我可以send_with_auth
无休止地调用而无需多次实例化 Foobar。完毕!