このようなスキームが必要な理由については省略し、その方法に焦点を当てます。
メンバー関数ポインターの代わりに、 を使用できますstd::function<double ()>
。これは、署名 を持つ呼び出し可能なエンティティの汎用ラッパーdouble foo()
です。std::function<double ()>
メンバー関数とオブジェクト インスタンスから outを作成するには、次のように使用std::bind
します。
std::function<double ()> callback =
std::bind(&Class::memberFunction, objectInstancePointer);
C++11 を使用していない場合、std::functionとstd::bindは Boost ::functionとboost::bindとして Boost でも利用できます。これらの Boost ドキュメントは、ほとんど (完全ではないにしても) 対応する C++11 に適用できます。
の代わりに、std::vector
を使用しstd::map
て getter を名前でインデックス付けできます。これは、パラメーター ID 番号の中央リストを維持するよりも実用的です。
パラメータの型が と異なる可能性がある場合は、 boost::anyまたはboost::variantを戻り値の型としてdouble
使用することを検討してください。
std::function
、std::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;
}