3

コードなしで説明すると、もっとあいまいになると思います。そこで、すべてをできるだけシンプルにしようとしたコードを次に示します。

#include <vector>
#include <iostream>

class WithParametersBase
{
public:
    WithParametersBase();

    double getX() const {return 0.0;}
    double getY() const {return 1.0;}

    //let's say I want to access these members using an unified interface:

    double getParameter(int index) const;

    // For example index == 0 means getX and index == 1 means getY.
    // I could implement it for example like this:

protected:

    void addGetter(double (WithParametersBase::* getter)()const)
    {
        getters_.push_back(getter);
    }

    std::vector<double (WithParametersBase::*)()const> getters_;
};

WithParametersBase::WithParametersBase()
{
    addGetter(&WithParametersBase::getX);
    addGetter(&WithParametersBase::getY);
}

double WithParametersBase::getParameter(int index) const
{
    return (this->*(getters_[index]))();
}

確かにそれは機能します。テスト プログラムの場合:

int main(int argc, char *argv[])
{
   WithParametersBase base;

   std::cout << base.getParameter(0)
             << base.getParameter(1) << std::endl;

   return 0;
}

印刷物は正しいです:

01

しかし、このクラスを拡張したい場合:

class WithParametersDerived : public WithParametersBase
{
public:
    WithParametersDerived();
    double getZ() const  {return 2.0;} // A new getter
};

WithParametersDerived::WithParametersDerived()
{
    // I want to integrate the new getter into the previous interface
    addGetter(&WithParametersDerived::getZ); 
}

私が電話した場合:

WithParametersDerived derived;
std::cout << derived.getParameter(2) << std::endl;

取得したい

2

プログラムをコンパイルできません。エラーが発生します:

error: no matching function for call to
'WithParametersDerived::addGetter
(double (WithParametersDerived::*)()const)'

これは合理的ですが、他に実装する方法がわかりません。

派生クラスの作成者が新しいゲッターを追加できるようにしたい。実行時にこれをすべて行うのはどういうわけか正しくないことはわかっていますが、テンプレートソリューションまたはプリプロセッサソリューションは見当たりません。いくつかの提案があれば、私に知らせてください。なんでも!

4

1 に答える 1

6

このようなスキームが必要な理由については省略し、その方法に焦点を当てます

メンバー関数ポインターの代わりに、 を使用できますstd::function<double ()>。これは、署名 を持つ呼び出し可能なエンティティの汎用ラッパーdouble foo()です。std::function<double ()>メンバー関数とオブジェクト インスタンスから outを作成するには、次のように使用std::bindします。

std::function<double ()> callback =
    std::bind(&Class::memberFunction, objectInstancePointer);

C++11 を使用していない場合、std::functionstd::bindは Boost ::functionboost::bindとして Boost でも利用できます。これらの Boost ドキュメントは、ほとんど (完全ではないにしても) 対応する C++11 に適用できます。

の代わりに、std::vectorを使用しstd::mapて getter を名前でインデックス付けできます。これは、パラメーター ID 番号の中央リストを維持するよりも実用的です。

パラメータの型が と異なる可能性がある場合は、 boost::anyまたはboost::variantを戻り値の型としてdouble使用することを検討してください。

std::functionstd::bind、およびを使用した完全な動作例を次に示しstd::mapます。

#include <cassert>
#include <map>
#include <iostream>
#include <functional>

class WithParametersBase
{
public:
    WithParametersBase()
    {
        addGetter("X", std::bind(&WithParametersBase::getX, this));
        addGetter("Y", std::bind(&WithParametersBase::getY, this));
    }

    virtual double getX() const {return 0.0;}
    virtual double getY() const {return 1.0;}

    // Access parameter by name
    double getParameter(const std::string& name) const
    {
        auto getterIter = getters_.find(name);
        assert(getterIter != getters_.end());
        return getterIter->second();
    }

protected:
    typedef std::function<double ()> ParameterGetter;
    typedef std::map<std::string, ParameterGetter> GetterMap;

    void addGetter(const std::string& name, const ParameterGetter& getter)
    {
        getters_[name] = getter;
    }

    GetterMap getters_;
};

class WithParametersDerived : public WithParametersBase
{
public:
    WithParametersDerived()
    {
        addGetter("Z", std::bind(&WithParametersDerived::getZ, this));

        // Override base class getX
        addGetter("X", std::bind(&WithParametersDerived::getX, this));
    }

    double getX() const {return 3.0;} 
    double getZ() const {return 2.0;} // A new getter
};

int main(int argc, char *argv[])
{
    WithParametersBase base;
    WithParametersDerived derived;
    WithParametersBase& polymorphic = derived;

    std::cout << base.getParameter("X")
              << base.getParameter("Y")
              << polymorphic.getParameter("X")
              << polymorphic.getParameter("Y")
              << polymorphic.getParameter("Z") << std::endl;

    return 0;
}

このアプローチの欠点は、WithParametersBase(または子孫)の各インスタンスにGetterMap. このようなオブジェクトが大量にある場合、それらすべてのメモリ オーバーヘッドGetterMapsが望ましくない場合があります。


std::functionと を廃止するより効率的なソリューションを次に示しstd::bindます。getter コールバックには、通常の関数ポインターと静的メンバー関数が使用されます。パラメーターが要求されるオブジェクト インスタンスは、これらの静的メンバー関数に引数として渡されます。派生型では、インスタンス参照は、実際の取得を行うメンバー関数を呼び出す前に、最初に派生型にダウンキャストされます。

オブジェクトごとではなく、GetterMap クラスごとに1 つだけになりました。静的な初期化順序の失敗getters()を回避するために、メソッドで「初回使用時に構築」イディオムを使用していることに注意してください。

このソリューションの欠点は、 から派生したクラスごとに記述する定型コードが増えることですWithParametersBase。テンプレートを使用してボイラープレート コードの量を削減できる可能性があります (マクロを使用すれば確実に可能です)。

#include <cassert>
#include <map>
#include <iostream>

class WithParametersBase
{
public:
    virtual double getX() const {return 0.0;}
    virtual double getY() const {return 1.0;}

    // Access parameter by name
    double getParameter(const std::string& name) const
    {
        auto getterIter = getters().find(name);
        assert(getterIter != getters().end());
        return getterIter->second(*this);
    }

protected:
    typedef double (*ParameterGetter)(const WithParametersBase& instance);
    typedef std::map<std::string, ParameterGetter> GetterMap;

    static double xGetter(const WithParametersBase& instance)
    {
        return instance.getX();
    }

    static double yGetter(const WithParametersBase& instance)
    {
        return instance.getY();
    }

    static GetterMap makeGetterMap()
    {
        GetterMap map;
        map["X"] = &WithParametersBase::xGetter;
        map["Y"] = &WithParametersBase::yGetter;
        return map;
    }

    virtual const GetterMap& getters() const
    {
        // Not thread-safe. Use std::call_once to make thread-safe.
        static GetterMap map = makeGetterMap();
        return map;
    };
};

class WithParametersDerived : public WithParametersBase
{
public:
    double getX() const {return 3.0;} 
    double getZ() const {return 2.0;} // A new getter

protected:
    static double zGetter(const WithParametersBase& instance)
    {
        // It's reasonably safe to assume that 'instance' is of type
        // WithParametersDerived, since WithParametersDerived was the one
        // that associated "Z" with this callback function.
        const WithParametersDerived& derived =
            dynamic_cast<const WithParametersDerived&>(instance);
        return derived.getZ();
    }

    static GetterMap makeGetterMap()
    {
        // We "inherit" the getter map from the base class before extending it.
        GetterMap map = WithParametersBase::makeGetterMap();
        map["Z"] = &WithParametersDerived::zGetter;
        return map;
    }

    virtual const GetterMap& getters() const
    {
        // Not thread-safe. Use std::call_once to make thread-safe.
        static GetterMap map = makeGetterMap();
        return map;
    };
};

int main(int argc, char *argv[])
{
    WithParametersBase base;
    WithParametersDerived derived;
    WithParametersBase& polymorphic = derived;

    std::cout << base.getParameter("X")
              << base.getParameter("Y")
              << polymorphic.getParameter("X")
              << polymorphic.getParameter("Y")
              << polymorphic.getParameter("Z") << std::endl;

    return 0;
}
于 2012-07-04T05:39:53.450 に答える