0

私は本物の C++ 初心者なので、しばらくお待ちください。まずは舞台を整えます。

binary.cpp次のようなバイナリにコンパイルされるC++ ソースがあります。

# 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 は 1 回だけ呼び出されるため、すべてが期待どおりに機能します。Foobar のインスタンスは 1 つだけで、send_auth複数回呼び出すことができます。

バイナリは私には役に立ちません。同じことを行う共有オブジェクト ライブラリが必要です。Foobar のインスタンスを 1 つだけ作成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 のインスタンスを 1 回だけ作成し、それを のすべての呼び出しに渡して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 のインスタンスを 1 つだけ作成し、それをライブラリが外部に公開する唯一のメソッドのすべての呼び出しに渡すという目標を達成するにはどうすればよいでしょうか。

誰でもそれを助けることができますか?よろしくフェリックス

アップデート

ライブラリで CommandlineParser を使用するのはばかげていました。実際には、単純に 2 つの 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への 2 回目の呼び出しですべてがクラッシュし、Foobar が再度インスタンス化されるという事実を除いて、ここではすべて正常に動作します。

更新 2:

最後に、さらに簡単に解決し、グローバルに利用可能な bool スイッチを使用して Foobar インスタンスを 1 回だけ初期化しました。

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 を複数回インスタンス化することなく、際限なく呼び出すことができます。終わり!

4

1 に答える 1

2

だから私の最初の提案はすることです

おそらくうまくいく最も簡単なこと

シングルトンを完全に取り除きます。ライブラリにグローバルな 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;
于 2013-07-25T15:41:44.810 に答える