How to use a C++ singleton to return an object initialized only once

836 views Asked by At

I am a real C++ noob, so please be patient with me. First lets set the stage.

I have a C++ source in binary.cpp that compiles to a binary which looks like:

# 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;
}

The send_auth method is loaded from another object code file and it gets passed an instance of foobar. The reason is, Foobar comes from an API object that I do not have the source of and which MUST NOT be instantiated more than once.

Since main is called only once, everything works as expected: There is only one instance of Foobar and send_auth can be called multiple times.

The binary is not of any use for me, I need a shared object library that does the same. It creates only one instance of Foobar and exposes an external interface method send_with_auth that can be called multiple times after my shared object lib was loaded.

My library code in library.cpp looks some like this:

# 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;
}

Since I load my shared object library via Ruby FFI I need some C-Style headers in library.h for my lib:

extern "C" {
  const char* send_with_auth(const char* X, const char* Y);
}

Now with stage set I have to create an instance of Foobar exactly once inside my library and pass it into every call of send_auth to not get a memory violation error from Foobar.

Here's my (overly complex) attempt with a singleton as I understood it. There is a new library.h like so:

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);
}

And there is this implementation 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;
}

This code at least compiles and I can bind together everything to a shared object library. Now when I load that library in an external process (a Ruby module using Ruby FFI in my case) I always get the error:

Could not open library '/some/path/libfoobar.so': /some/path/libfoobar.so: undefined symbol: _ZN9Singleton2erE (LoadError)

I'm quite sure that my compiling/binding/stripping process from library.o to libfoobar.so is ok, because it succeeds in other cases. I am quite sure that I misunderstand C++'s singleton concept here. I wonder how I can achive my goal: create only one instance of Foobar inside my shared object library and pass it into every call of the only methods my library exposes to the outside.

Can anyone help on that? Regards Felix

Update

Using the CommandlineParser in the library was stupid. In fact it simply returned two C-strings. The API library path and a log dir. With that I recreated a namespace:

namespace
{
  char* home("/path/to/api/libs");
  char* log("/tmp");
  Foobar foobar(home, log);
}

That leads to a seg fault in the moment when I load the library. In contrast to that I can put these lines into my function directly:

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;
}

Everything works fine here except the fact that a second call to send_with_authlets crash everything cause Foobar is instantiated again.

Update 2:

Finally I solved it even simpler, used a globally available bool switch to initialize my Foobar instance only once:

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();
}

Now I can call send_with_auth endlessly without having Foobar instantiated more than once. Done!

1

There are 1 answers

6
Useless On BEST ANSWER

So my first suggestion is to do

The simplest thing that could possibly work

and get rid of the singleton entirely. Just create a global Foobar object in your library:

// library.cpp
namespace {
    Foobar global_foobar;
}

I've put it in an anonymous namespace so it won't clash with any other symbol called "global_foobar" in the main executable, or another library.

This does mean it can only be accessed from within library.cpp. If you have more than one .cpp file linked into your lib, you'll need something marginally more elaborate:

// 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;