2

パイプラインのようなデザイン パターンに取り組んでいます。私の設計目標の 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を手動で定義しているのに対し、この手順を自動化したいことです。

上記の設計と大きく異なる設計ソリューションは考慮していないことに注意してください。


ここでは、この問題を解決するためのいくつかの可能なアプローチについて、いくつかの考えを示します。現在検討中のソリューションの計画を作成しました。この質問に答えようとする場合に役立つ可能性があります。

  1. という名前のユーザー定義の部分的に特殊化された構造の数を取得しますgetOutputImpl
  2. そのような構造体ごとに、 という名前のメンバーの出力タイプを決定しますapply
  3. OutputPort関連する署名を持つ関数へのポインターを作成し、それらをベクトルに追加する (再帰的な) メタテンプレート プロシージャを設定します。

上記の手順 1 ~ 3 はすべてコンパイル時に実行する必要があると思います。データ出力クラスを設計するユーザーの介入が必要ない場合は、ソリューションの美学については気にしません。ただし、カスタム コンパイラ マクロは使用したくありません。

この投稿は、メンバー関数の署名を推測する方法を示しています。これは役立つ場合があります。

4

2 に答える 2

0
using AO=AlgorithmOutput;
template<size_t N>
using R=decltype(std::declval<AO*>()->getOutput<N>(0));
template<size_t... Is>
std::vector< boost::any > make_vec( std::index_sequence<Is...> ){
  return {
    boost::any(
      std::function< R<Is>(int) >{
        [this](int x){return this->getOutput<N>(x);}
      }
    )...
  };
}

C++14 型を使用しますが、書きやすい型です。それを渡すとstd::make_index_sequence<Count>{}、あなたのapply.

あなたの設計は、静的ディスパッチ、動的ディスパッチ、型消去、および非効率的なポインター セマンティクスの混乱のように見えますが、(慈善的に) これははるかに複雑な問題の単純化に過ぎず、設計は保証されています。

ただし、静的引数が正しくないという実行時チェックは有効ではありません。実行時にコンパイル時エラーをチェックしません。

static auto apply( PipeOutputClass const* p_Self, int c_Idx ) { throw std::runtime_error("Wrong template argument."); }

これは無意味に思えます: コンパイルしてスローするのではなく、コンパイルに失敗するようにします。

于 2015-04-19T19:11:29.903 に答える