パイプラインのようなデザイン パターンに取り組んでいます。私の設計目標の 1 つは、特定のデータ クラスの関数メンバーへのポインターを提供することにより、パイプライン セグメントの動的リンクを有効にすることです。
各データ クラスには、整数テンプレート引数を使用してインデックス付けされた一連の関数メンバー (データ クラスの出力ポートを表す) があります。これらの関数は、キーワードを使用して戻り値の型を動的に推測しますauto
が、すべて同じ整数引数を受け入れc_Idx
ますtemplate <int N> auto getOutput(int c_Idx) const
。各関数に関連付けられた機能getOutput
は、部分的に特殊化された構造のセットで (ユーザーによって) 定義されますgetOutputImpl
。したがって、各データ クラスは、1 から一定数K
までの出力データ ポートを持つことができます。
一般的な方法でパイプライン セグメント間の動的リンクを可能にするために、タイプ のコンテナに格納できますstd::vector<boost::any>
。ただし、このベクトルに関数メンバー テンプレートへのポインターを自動的に入力できるようにする必要があります。
手動実装の例を以下に示します
template<class TLeafType>
class AlgorithmOutput
{
protected:
std::vector<boost::any> OutputPorts;
public:
AlgorithmOutput()
{
//////////////////////////////////////////
// This procedure needs to be automated //
//////////////////////////////////////////
std::function<std::unique_ptr<double>(int)> pOutFun1 = std::bind(
std::mem_fn(
true ? &AlgorithmOutput<TLeafType>::getOutput<0> : nullptr
),
this,
std::placeholders::_1
);
OutputPorts.push_back(pOutFun1);
std::function<std::unique_ptr<int>(int)> pOutFun2 = std::bind(
std::mem_fn(
true ? &AlgorithmOutput<TLeafType>::getOutput<1> : nullptr
),
this,
std::placeholders::_1
);
OutputPorts.push_back(pOutFun2);
}
virtual ~AlgorithmOutput() {}
protected:
TLeafType* asLeaf(void)
{
return static_cast<TLeafType*>(this);
}
TLeafType const* asLeaf(void) const
{
return static_cast<TLeafType const*>(this);
}
public:
template <int N>
auto getOutput(int c_Idx) const
{
return asLeaf() -> getOutput<N>(c_Idx);
}
boost::any getOutputPort(int PortIdx)
{
return OutputPorts[PortIdx];
}
};
class PipeOutputClass: public AlgorithmOutput<PipeOutputClass>
{
public:
template <int N>
auto getOutput(int c_Idx) const
{
return getOutputImpl<N>::apply(this, c_Idx);
}
template<int N, typename S> friend struct getOutputImpl;
template<int N, typename = void>
struct getOutputImpl
{
static auto apply(
PipeOutputClass const* p_Self,
int c_Idx
)
{ throw std::runtime_error("Wrong template argument."); }
};
template <typename S>
struct getOutputImpl<0, S>
{
static std::unique_ptr<double> apply(
PipeOutputClass const* p_Self,
int c_Idx
)
{
std::unique_ptr<double> mydouble(new double(10));
return mydouble;
}
};
template <typename S>
struct getOutputImpl<1, S>
{
static std::unique_ptr<int > apply(
PipeOutputClass const* p_Self,
int c_Idx
)
{
std::unique_ptr<int > myint(new int(3));
return myint;
}
};
};
上記の例の問題点は、メンバー関数ポインターpOutFunX
を手動で定義しているのに対し、この手順を自動化したいことです。
上記の設計と大きく異なる設計ソリューションは考慮していないことに注意してください。
ここでは、この問題を解決するためのいくつかの可能なアプローチについて、いくつかの考えを示します。現在検討中のソリューションの計画を作成しました。この質問に答えようとする場合に役立つ可能性があります。
- という名前のユーザー定義の部分的に特殊化された構造の数を取得します
getOutputImpl
。 - そのような構造体ごとに、 という名前のメンバーの出力タイプを決定します
apply
。 OutputPort
関連する署名を持つ関数へのポインターを作成し、それらをベクトルに追加する (再帰的な) メタテンプレート プロシージャを設定します。
上記の手順 1 ~ 3 はすべてコンパイル時に実行する必要があると思います。データ出力クラスを設計するユーザーの介入が必要ない場合は、ソリューションの美学については気にしません。ただし、カスタム コンパイラ マクロは使用したくありません。
この投稿は、メンバー関数の署名を推測する方法を示しています。これは役立つ場合があります。