2017-02-02 12 views
0

main()がオブジェクトMyAppをインスタンス化し、そのオブジェクトをReadConfig関数に渡すC++アプリケーションがあります。関数ポインタを使用して異なるシグネチャでC++関数を呼び出す方法

ReadConfigは、テキストベースのファイルを開き、解析し、適切なMyAppメソッドを呼び出して設定します。

class MyApp 
{ 
private: 

public: 
    void SetRate(uint16_t); 
    void EnableLogging(bool); 
    void SetAddress(uint32_t); 
}; 

私は、新しい公開メソッドがMyAppに追加されると、テーブルを更新するなどの簡単な操作が可能なように、ReadConfigのメンテナンスを容易にしようとしています。私は以下の解決策を思いついたが、私はそれが好きではない。私が正しい場所に0を入れていることを確認する必要があるため、維持するのは難しいです。

以下は、私が解決した例です。これをより良くするためのアドバイスは高く評価されます。私が組み込みで作業するので、使用するC++コンパイラはC++をサポートしていません14 &追加機能はありません。そして、私はこれを自分でやることの仕組みを理解するために、STLライブラリの使用を避けたいと思います。ここで

は、私が持っているものです:私は、設定ファイルを読み込み、それを解析として

enum ARGTYPE {TBOOL, TUINT16, TUINT32}; 

template<typename TOBJ, typename TARG> 
struct TSetting 
{ 
    void (TOBJ::*FSet)(TARG); 
}; 

template<typename obj> 
struct SETTINGFN 
{ 
    const char      *setting_name; 
    ARGTYPE       Targ; 
    TSetting<obj,bool>    HBool; 
    TSetting<obj,uint16_t>   HUint16; 
    TSetting<obj,uint32_t>   HUint32; 
}; 

SETTINGFN<MyApp> MyAppSettings[] = 
{ 
    "logging" ,TBOOL,  &MyApp::EnableLogging, 0,0,0 
    ,"maxrate" ,TUINT16,  0,0,0,&MyApp::SetRate 
    ,"address" ,TUINT32,  0, &MyApp::SetAddress, 0,0 

}; 
unsigned int MyAppSettings_Count = sizeof(MyAppSettings)/sizeof(SETTINGFN<MyApp>); 

はその後、私は、関数ポインタを通じて、実際のMyApp関数を呼び出す処理するための関数を呼び出します。その機能は次のとおりです。

bool AppSetting(MyApp &E, TObjnode &node) 
{ 
    bool rval = false; 

    for(unsigned int i=0; i<MyAppSettings_Count && !rval; i++) 
    { 
    if(node.GetName() == MyAppSettings[i].setting_name) 
    { 
     rval = true; 

     switch(MyAppSettings[i].Targ) 
     { 
     case TBOOL: 
      (E.*MyAppSettings[i].HBool.FSet)(node.GetValue().AsBool()); 
     break; 

     case TUINT16: 
      (E.*MyAppSettings[i].HUint16.FSet)(node.GetValue().Value()); 
     break; 

     case TUINT32: 
      (E.*MyAppSettings[i].HUint32.FSet)(node.GetValue().Value()); 
     break; 
     } 
    } 
    } 

    return(rval); 
} 
+0

?すべてのセッターは同じタイプにすることができます。文字列を受け取り、解析関数と型保証セッターの両方を呼び出すトランポリンはもっと良いかもしれません。 –

+1

...新しいプロパティ/セッターが追加されるたびに、コンフィギュレーションディスパッチルーチンに関数呼び出しを追加する必要を避けるために、新しいプロパティ/セッターが追加されるたびにプロプライエタリデータ構造を更新したいですか?それぞれ自分自身に... –

+0

私はこれらのことを考えましたが、現在の関数のシグネチャを変更したり新しいものを追加することを避けることを望んでいました – Eric

答えて

2

あなたのデザインのトーンは、異なるパラメータタイプです。

パラメータが構造体に抽象化されている場合、関数は1つの固定シグネチャに簡略化できます。

ベース構造を用いることにより

、署名およびより一般的である。

struct Arguments_Base 
{ 
}; 

void SetRate(Arguments_Base& ab); 
void EnableLogging(Arguments_Base& ab); 
void SetAddress(Arguments_Base& ab); 

均一なシグネチャを有することにより、関数ポインタまたは機能はが容易に検索作り、テーブルまたはマップに使用できるオブジェクト。検索エンジンは一般的であり、検索エンジンではなくデータ(例えばテーブル)のみを変更する必要があるように、データのサイズに依存する必要がある。

各タイプの引数の集合は、Arguments_Baseクラスから派生する必要があります。関数は、dynamic_castベースクラスの参照を次に行うことができます。また

参照:工場デザインパターン、ビジターデザインパターン、ダブルディスパッチ各セッターに文字列を渡すと、彼らはタイプ固有の再利用可能な解析コードを呼び出すさせると考えられ

+0

それを信じてもいなくても、私はこれを考えましたが、現在の関数シグネチャを変更したり新しいシグネチャシグネチャを追加することを避けたいと考えていました。 – Eric

+0

ヤック。 dynamic_cast。設定ファイルの読み込みにも。 – rubenvb

+0

@rubenvb基本クラスが派生クラスがオーバーロードする可能性のあるメソッドを提供する場合、動的キャストは必要ありません。 NVIのデザインパターンを考えてください。代わりに、Ben Voigtのように、Arguments_Baseが値(int、boolなど)を返すことができるStringクラスである場合、基本クラスは単に変換を行うことができます – Eric

関連する問題