如何为类的继承层次结构的getter创建统一接口?

时间:2012-07-04 04:30:06

标签: 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]))();
}

确实有效。有了测试程序:

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)'

哪个是合理的,但我不知道如何实现它。

我希望派生类的创建者能够添加新的getter。我知道,它在某种程度上不能在运行时完成所有这些,但我没有看到模板解决方案或预处理器解决方案。如果您有任何建议,请告诉我。什么!

1 个答案:

答案 0 :(得分:6)

我会回避为什么你需要这样的方案,并专注于如何

您可以使用std::function<double ()>代替成员函数指针,double foo()是具有签名std::function<double ()>的任何可调用实体的通用包装器。要从成员函数和对象实例创建std::bind,请使用std::function<double ()> callback = std::bind(&Class::memberFunction, objectInstancePointer); ,如下所示:

std::vector

如果您不使用C ++ 11,则std::functionstd::bind也可以作为boost::functionboost::bind在Boost中使用。这些Boost文档大部分(如果不是完全)适用于他们的C ++ 11版本。

您可以使用std::map按名称索引getter,而不是double。这可能比维护参数ID号的中心列表更实用。

如果您的参数的类型与std::function不同,那么您可能需要考虑使用boost::anyboost::variant作为返回类型。

以下是使用std::bindstd::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::bindGetterMap。常规函数指针和静态成员函数用于getter回调。请求参数的对象实例作为参数传递给这些静态成员函数。在派生类型中,在调用实际获取的成员函数之前,首先将实例引用向下转换为派生类型。

现在每个类只有一个getters() 而不是每个对象。请注意WithParametersBase方法中使用“首次使用时构造”的习惯用法以避免使用static initialization order fiasco

此解决方案的缺点是,为#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; } 派生的每个类编写了更多的样板代码。有可能使用模板减少样板代码的数量(绝对可以使用宏)。

{{1}}