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

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

我希望派生类的创建者能够添加新的 getter。我知道,在运行时做这一切感觉有点不对劲,但我没有看到模板解决方案或预处理器解决方案。如果您有一些建议,请告诉我。任何事物!

4

1 回答 1

6

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

您可以使用 a 代替成员函数指针,std::function<double ()>它是任何具有签名的可调用实体的通用包装器double foo()。要创建一个std::function<double ()>成员函数和一个对象实例,您可以使用std::bind如下:

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

如果您不使用 C++11,std::functionstd::bind在 Boost 中也可以作为boost::functionboost::bind使用。这些的 Boost 文档大多(如果不是完全)适用于它们的 C++11 对应物。

代替 a std::vector,您可以使用 astd::map按名称索引 getter。这可能比维护参数 ID 号的中央列表更实用。

如果您的参数的类型可能与 不同double,那么您可能需要考虑使用boost::anyboost::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;
}

这种方法的缺点是WithParametersBase(或后代)的每个实例都将包含一个GetterMap. 如果您有大量此类对象,则所有这些对象的内存开销GetterMaps可能都是不可取的。


这是一个更有效的解决方案,它取消了std::functionand std::bind。常规函数指针和静态成员函数用于 getter 回调。请求参数的对象实例作为参数传递给这些静态成员函数。在派生类型中,实例引用首先向下转换为派生类型,然后再调用执行实际获取的成员函数。

现在GetterMap 每个类而不是每个对象只有一个。注意在方法中使用“construct on first use”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 回答