2009-05-10 16 views
1

私はC++フレームワーク用のラッパーを作成したいと思います。このフレームワークはちょっとバギーで、C++ではうまくいきません。だから私は彼らのフレームワークの外側から(良い古いCファイルを介して)彼らのメソッドを呼び出すことができるようにしたいと思います。これは、C++の代わりにCで使用するために必要なフレームワークメソッドをカプセル化するラッパーの必要性のように思えます。Cプログラム内でC++共有ライブラリを呼び出す...管理する方法は?

....ここでは、私はすでにやったことでこれまでのところは良い:(このフォルダを含める私、超音波法は、フレームワークの外から呼ばれるべきである)

インタフェース aldebaran.h :

#ifndef _ALDEBARAN_H 
#define _ALDEBARAN_H 

#ifdef __cplusplus 
extern "C" { 
#endif 

void subscribe_ultrasound(); 
void unsubscribe_ultrasound(); 
float read_ultrasound(); 

#ifdef __cplusplus 
} 
#endif 

#endif 

今ラッパー:

のcppファイルaldebaran.cpp:

aldebaran.cppため
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#include "aldebaran.h" 
#include "alproxy.h" 
#include "../../include/aldebaran.h" 

/* 
* Ultrasound defines 
*/ 
#define ULTRASOUND_RESERVATION_MAGIC "magic_foobar" 

#define ULTRASOUND_POLL_TIME   250 
#define ULTRASOUND_READ_ATTEMPTS  50 
#define ULTRASOUND_SLEEP_TIME   20 

using namespace std; 
using namespace AL; 

/* 
* Framework proxies 
*/ 
ALPtr<ALProxy> al_tts; 
ALPtr<ALProxy> al_led; 
ALPtr<ALProxy> al_motion; 
ALPtr<ALProxy> al_mem; 
ALPtr<ALProxy> al_us; 
ALPtr<ALProxy> al_cam; 
ALPtr<ALProxy> al_dcm; 

/* 
* Constructor 
*/ 
Aldebaran::Aldebaran(ALPtr<ALBroker> pBroker, std::string pName): ALModule(pBroker, pName) 
{ 
    try { 
     al_tts = this->getParentBroker()->getProxy("ALTextToSpeech"); 
     al_led = this->getParentBroker()->getProxy("ALLeds"); 
     al_motion = this->getParentBroker()->getProxy("ALMotion"); 

     al_mem = this->getParentBroker()->getProxy("ALMemory"); 
     al_us  = this->getParentBroker()->getProxy("ALUltraSound"); 
     al_cam = this->getParentBroker()->getProxy("NaoCam"); 
     al_dcm = this->getParentBroker()->getProxy("DCM"); 
    }catch(ALError& err){ 
     std::cout << "XXX: ERROR: " << err.toString() << std::endl; 
     return 1; 
    } 

    printf("XXX: module aldebaran initiated\n"); 
    fflush(0); 
} 

/* 
* Destructor 
*/ 
Aldebaran::~Aldebaran() 
{ 
    printf("XXX: module aldebaran destructed\n"); 
    fflush(0); 
} 

/* 
* Subscribe to ultrasound module 
*/ 
void subscribe_ultrasound() 
{ 
    ALValue param; 

    param.arrayPush(ULTRASOUND_POLL_TIME); 
    al_us->callVoid("subscribe", string(ULTRASOUND_RESERVATION_MAGIC), param); 

    printf("XXX: ultrasound subscribed: %s\n", ULTRASOUND_RESERVATION_MAGIC); 
    fflush(0); 
} 

/* 
* Unsubscribe to ultrasound module 
*/ 
void unsubscribe_ultrasound() 
{ 
    al_us->callVoid("unsubscribe", string(ULTRASOUND_RESERVATION_MAGIC)); 

    printf("XXX: ultrasound unsubscribed: %s\n", ULTRASOUND_RESERVATION_MAGIC); 
    fflush(0); 
} 

/* 
* Read from ultrasound module 
*/ 
float read_ultrasound() 
{ 
    int i; 
    float val1, val2; 
    float val_sum; 
    ALValue distance; 

    val_sum = .0f; 

    for(i = 0; i < ULTRASOUND_READ_ATTEMPTS; ++i){ 
     SleepMs(ULTRASOUND_SLEEP_TIME); 

     distance = al_mem->call<ALValue>("getData", string("extractors/alultrasound/distances")); 
     sscanf(distance.toString(AL::VerbosityMini).c_str(),"[%f, %f, \"object\"]", &val1, &val2); 

     val_sum += val1; 
    } 

    return val_sum/(1.f * ULTRASOUND_READ_ATTEMPTS); 
} 

定義ファイル:

#ifndef ALDEBARAN_API_H 
#define ALDEBARAN_API_H 

#include <string> 

#include "al_starter.h" 
#include "alptr.h" 

using namespace AL; 

class Aldebaran : public AL::ALModule 
{ 
    public: 
     Aldebaran(ALPtr<ALBroker> pBroker, std::string pName); 

     virtual ~Aldebaran(); 

     std::string version(){ return ALTOOLS_VERSION(ALDEBARAN); }; 

     bool innerTest(){ return true; }; 
}; 

#endif 

だからこれは私のラッパーの簡単な例である必要があり、それがlibaldebaran.soする罰金コンパイルします。

Cで、今私のテストプログラム:

...今、私はこのような単純なCファイルからメソッドaldebaran.hインターフェースを呼び出すしたいと思います:

#include <stdio.h> 

/* 
* Begin your includes here... 
*/ 

#include "../include/aldebaran.h" 

/* 
* End your includes here... 
*/ 

#define TEST_OKAY 1 
#define TEST_FAILED 0 

#define TEST_NAME "test_libaldebaran" 

unsigned int count_all = 0; 
unsigned int count_ok = 0; 

const char *__test_print(int x) 
{ 
    count_all++; 

    if(x == 1){ 
     count_ok++; 

     return "ok"; 
    } 

    return "failed"; 
} 

/* 
* Begin tests here... 
*/ 

int test_subscribe_ultrasound() 
{ 
    subscribe_ultrasound(); 

    return TEST_OKAY; 
} 

int test_unsubscribe_ultrasound() 
{ 
    unsubscribe_ultrasound(); 

    return TEST_OKAY; 
} 

int test_read_ultrasound() 
{ 
    float i; 

    i = read_ultrasound(); 

    return (i > .0f ? TEST_OKAY : TEST_FAILED); 
} 

/* 
* Execute tests here... 
*/ 

int main(int argc, char **argv) 
{ 
    printf("running test: %s\n\n", TEST_NAME); 

    printf("test_subscribe_ultrasound: \t %s\n", __test_print(test_subscribe_ultrasound())); 
    printf("test_read_ultrasound:   \t %s\n", __test_print(test_read_ultrasound())); 
    printf("test_unsubscribe_ultrasound: \t %s\n", __test_print(test_unsubscribe_ultrasound())); 

    printf("test finished: %s has %u/%u tests passed\n\n", TEST_NAME, count_ok, count_all); 

    return (count_all - count_ok); 
} 

どのようにすることができます私はこれらのメソッドを呼び出すことができますか?私は私のCファイル内に私はそのようなオブジェクトインスタンス(それはすべての必要なALProxiesを生成する)を作成する可能性がないという意味ですか?

ヘルプは本当にいただければ幸いです... THX


は、これまでどうもありがとうございました!!

xtoflは..私は(別のCなしで、好ましく++オブジェクト)できるだけ簡単に私のインターフェイスを維持したいと思い言ったように:

#ifndef _ALDEBARAN_H 
#define _ALDEBARAN_H 

#ifdef __cplusplus 
extern "C" { 
#endif 

void subscribe_ultrasound(); 
void unsubscribe_ultrasound(); 
float read_ultrasound(); 

#ifdef __cplusplus 
} 
#endif 

#endif 

問題はここsubscribe_ultrasound()のような機能をすることができないということですすべてのプロキシのインスタンス化せずに呼ばれる...これは私たちの前提条件である:

... 
     al_tts = this->getParentBroker()->getProxy("ALTextToSpeech"); 
     al_led = this->getParentBroker()->getProxy("ALLeds"); 
     al_motion = this->getParentBroker()->getProxy("ALMotion"); 

     al_mem = this->getParentBroker()->getProxy("ALMemory"); 
     al_us  = this->getParentBroker()->getProxy("ALUltraSound"); 
     al_cam = this->getParentBroker()->getProxy("NaoCam"); 
     al_dcm = this->getParentBroker()->getProxy("DCM"); 
... 

私はと呼ばれる上記のコードをお持ちでない場合は、他のすべてが失敗します。

その枠組みの中で、このコールのようなPythonスクリプトを経由して「自動ロード」私libaldebaran.soすることが可能である:フレームワークログ

myModule = ALProxy("Aldebaran", global_params.strRemoteIP, global_params.nRemotePort); 

そして、こう述べています。

May 10 15:02:44 Hunter user.notice root: XXX: module aldebaran initiated 
May 10 15:02:46 Hunter user.notice root: INFO: Registering module : 'Aldebaran' 
May 10 15:02:46 Hunter user.notice root: ______ End of loading libraries ______ 

全く大丈夫です...それは私のモジュールのコンストラクタと呼ばれています(他のすべての必要なプロキシもインスタンス化されています)。

もちろん、この場合は

は多分他のすべてのプロセスにこれを共有する可能性がある...私のCプログラムに属していませんか?

+0

私は「悪い」C++をC++の「良い」C++で包んでいないのではないかと不思議です。 –

答えて

1

あなたは、OOフレームワークの周りにCラッパーAPIを作成すると言っています。

これは、ラッパーAPIを渡すオブジェクト(デコレートされたヘッダーから表示される)を必要としないことを意味します。テストプログラムから見て、必要なオブジェクトはすべて、ラッパーAPIの背後に作成/破棄されているようです。

最初のケースと思われます。ラッパーAPIをテストするためにオブジェクトは必要ありません。最終的には、すべてのオブジェクトは固定された関数セットを介してアクセスされるバイト(メモリ内)です。関数がメンバ関数(C++)として記述されているのか、プレーンC関数として記述されているのかは、オブジェクトの意図されたセマンティクスに従う限り重要ではありません。

6

多少異なるアプローチをとってみたいかもしれません。あなたのCインタフェースのために、このような何かを考えてみましょう:あなたは、あなたがそれになりたいものは何でもする実装では、それを定義することができるように

#ifdef __cplusplus 
extern "C" { 
#endif 

struct UltrasoundHandle; 

UltrasoundHandle* ultrasound_Create(); 
void ultrasound_Destroy(UltrasoundHandle *self): 
void ultrasound_Subscribe(UltrasoundHandle *self); 
void ultrasound_Unsubscribe(UltrasoundHandle *self); 
float ultrasound_Read(UltrasoundHandle *self); 

#ifdef __cplusplus 
} 
#endif 

UltrasoundHandle構造は、意図的に不透明です。私が作ったもう一つの変更は、コンストラクタとデストラクタに似た明示的な作成メソッドと破棄メソッドを追加することでした。以下のような実装では、になります:キーがCのためのC++インタフェースをラップにある

extern "C" { 

struct UltrasoundHandle { 
    UltrasoundHandle() { 
     // do per instance initializations here 
    } 
    ~UltrasoundHandle() { 
     // do per instance cleanup here 
    } 
    void subscribe() { 
    } 
    void unsubscribe() { 
    } 
    float read() { 
    } 
}; 


static int HandleCounter = 0; 


UltrasoundHandle* ultrasound_Create() { 
    try { 
     if (HandleCounter++ == 1) { 
      // perform global initializations here 
     } 
     return new UltrasoundHandle; 
    } catch (...) { 
     // log error 
    } 
    return NULL; 
} 

void ultrasound_Destroy(UltrasoundHandle *self) { 
    try { 
     delete self; 
     if (--HandleCounter == 0) { 
      // perform global teardown here 
     } 
    } catch (...) { 
     // log error 
    } 
} 

呼び出し側が明示的に関数にオブジェクトのポインタ(this)を渡し自由関数を通じてオブジェクト指向の概念を公開するために、明示的に公開することですコンストラクタとデストラクタを同じ方法で使用します。ラッパーコードは、そこからほぼ機械的に生成することができます。その他の重要な点は、例外がグローバルオブジェクトインスタンスの外側に伝播することを決して許さないことです。後者があなたに悲しみを引き起こすかどうかはわかりませんが、私は建設/破壊命令の問題に懸念しています。

0

あなたがこれを認識しているかどうかは分かりませんが、プログラムに動的にロードするC++コードがある場合は、プログラムをC++コンパイラにリンクしてメイン関数をC++関数にする必要がありますそれはのように些細な場合でも:

int main(int argc, char **argv) 
{ 
    return(real_main_in_c(argc, argv)); 
} 

real_main_in_c()機能は、以前main()と呼ばれるものです。それは単に名前が変更されました。これにより、グローバル変数や静的変数などの初期化を処理するためのC++メカニズムがロードされ、操作可能になります。 C++の起動コードは、Cの起動コードよりも多く機能します。動的にCをロードする

これは答えのほんの1つ(小)の側面ですが、これは重要な実用的なものです。

+0

私は間違っている可能性がありますが、C++ライブラリの動的リンケージは、ライブラリがロードされたときにすべてのグローバル静的初期化が行われることを保証します(GCCの_init/_fini、MSVCのDllMainなど) – Tom

+0

使用しているシステム; AFAICRには、このアドバイスに従う理由(数年前、5年前のように)がありました。動的にロードされたC++ライブラリを使用して切り替えたときに、リンケージを変更する必要がありました。単にmain()の名前を変更しなくても、C++コンパイラにリンクするだけで十分です。 –

0

できるだけシンプルにしてください。私はあなたがここでそれを単純すぎるようにしようとしていると思っています。それをすべてラップしますが、C++コンパイラを使用してください。

したがって、C++コンパイラを使用してラッパーを作成した場合、サブスクライブ関数内のオブジェクトをインスタンス化し、すべてを静的(またはグローバル)オブジェクトを使用してサブスクライブ解除ですべて解放することができます。シンプルに表現したい3つの関数は、 "extern C"でラップされ、C++オブジェクトをカプセル化しながら、呼び出し元に公開されたCスタイルのインターフェイスを持ちます。

すべてのプロキシをインスタンス化する別の関数が必要な場合は、それを追加します。また、まだ存在しない場合は作成して、登録する最初の呼び出しで常に作成されるようにします。

ここで、プロキシオブジェクトがインスタンス単位で必要な場合(つまり、2人の発信者が購読を希望し、発信者ごとに一意のプロキシが必要な場合)、そのオブジェクトをコレクション(私は地図を示唆しています)、あなたが作るすべての呼び出しは、マップから呼び出しごとのオブジェクトを抽出するために使用する 'ハンドル'または 'sessionid'を渡す必要があります。

関連する問題