Я настоящий нубик 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
это может быть вызвано несколько раз после загрузки моего общего объекта lib.
Код моей библиотеки в 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-Style в 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 с использованием Ruby FFI в моем случае), я всегда получаю ошибку:
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
позволяет сбить все, потому что Foobar снова создается.
Наконец, я решил ее еще проще: использовал глобально доступный переключатель 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 более одного раза. Готово!
Итак, мое первое предложение — сделать
и избавиться от синглтона полностью. Просто создайте глобальный объект Foobar в вашей библиотеке:
// library.cpp
namespace {
Foobar global_foobar;
}
Я поместил его в анонимное пространство имен, чтобы он не конфликтовал с любым другим символом, называемым «global_foobar», в основном исполняемом файле или другой библиотеке.
Это означает, что к нему можно получить доступ только из library.cpp. Если у вас есть более одного файла .cpp, связанного с вашей библиотекой, вам понадобится что-то более сложное:
// library_internal.h
namespace my_unique_library_namespace {
extern Foobar global_foobar;
}
// library.cpp
#include "library_internal.h"namespace my_unique_library_namespace {
Foobar global_foobar;
}
// library_2.cpp
#include "library_internal.h"using my_unique_library_namespace::global_foobar;
Других решений пока нет …