Mar*_*dik 3 c++ templates pointers c-preprocessor
我认为没有代码的任何解释都会更加模糊.所以这里的代码是我尽量保持一切尽可能简单的代码.
#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]))();
}
Run Code Online (Sandbox Code Playgroud)
确实有效.有了测试程序:
int main(int argc, char *argv[])
{
WithParametersBase base;
std::cout << base.getParameter(0)
<< base.getParameter(1) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
打印输出是正确的:
01
Run Code Online (Sandbox Code Playgroud)
但是如果我想扩展这个类:
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);
}
Run Code Online (Sandbox Code Playgroud)
如果我打电话:
WithParametersDerived derived;
std::cout << derived.getParameter(2) << std::endl;
Run Code Online (Sandbox Code Playgroud)
我想得到一个
2
Run Code Online (Sandbox Code Playgroud)
我无法编译程序.我收到一个错误:
error: no matching function for call to
'WithParametersDerived::addGetter
(double (WithParametersDerived::*)()const)'
Run Code Online (Sandbox Code Playgroud)
这是合理的,但我不知道如何实现它.
我希望派生类的创建者能够只添加新的getter.我知道,它在某种程度上不能在运行时完成所有这些,但我没有看到模板解决方案或预处理器解决方案.如果您有任何建议,请告诉我.什么!
我会回避你为什么需要这样一个计划,并专注于如何.
您可以使用a std::function<double ()>,而不是成员函数指针,它是具有签名的任何可调用实体的通用包装器double foo().要创建std::function<double ()>成员函数和对象实例,请std::bind按如下方式使用:
std::function<double ()> callback =
std::bind(&Class::memberFunction, objectInstancePointer);
Run Code Online (Sandbox Code Playgroud)
如果您不使用C++ 11,则std :: function和std :: bind在Boost中也可用作boost :: function和boost :: bind.这些Boost文档大部分(如果不是完全)适用于他们的C++ 11版本.
std::vector您可以使用a std::map来按名称索引getter,而不是a .这可能比维护参数ID号的中心列表更实用.
如果您的参数类型不同double,那么您可能需要考虑使用boost :: any或boost :: variant作为返回类型.
下面是一个完整的工作示例使用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;
}
Run Code Online (Sandbox Code Playgroud)
这种方法的缺点是每个WithParametersBase(或后代)实例都包含一个GetterMap.如果您有大量此类对象,则所有这些对象的内存开销GetterMaps可能是不合需要的.
这是一个更有效的解决方案,可以消除std::function和std::bind.常规函数指针和静态成员函数用于getter回调.请求参数的对象实例作为参数传递给这些静态成员函数.在派生类型中,在调用实际获取的成员函数之前,首先将实例引用向下转换为派生类型.
现在GetterMap 每个类只有一个而不是每个对象.注意在方法中使用"构造首次使用"的习惯用法,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;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
407 次 |
| 最近记录: |