Как использовать синглтон C++ для возврата объекта, инициализированного только один раз

Я настоящий нубик 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-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 снова создается.

Обновление 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 более одного раза. Готово!

1 ответ

Решение

Итак, мое первое предложение - сделать

Самая простая вещь, которая могла бы работать

и избавиться от синглтона полностью. Просто создайте глобальный объект 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;
Другие вопросы по тегам