对虚拟函数进行模板化,从单个类管理不同的类属性

时间:2018-06-28 14:27:54

标签: c++ oop templates interface template-meta-programming

在某个地方,我觉得需要通过类似INI的接口通过单个类更清楚地管理不同的类,所以我写了

#include <vector>
#include <string>
#include <memory>
#include <iostream>

struct A {
    using getter = int(A::*)();
    using setter = void(A::*)(int);

    virtual std::string get_name() = 0;
    virtual getter get_getter(std::string property_name) = 0;
};

struct PropertyManager {
    template<class Type>
    using pointer = std::shared_ptr<Type>;
    using string_type = std::string;
    int get_property(string_type object_name,string_type property_name) {
        A::getter property_getter = nullptr;
        pointer<A> mo;
        for(auto& o : objects) {
            if (o->get_name() == object_name) {
                mo = o;
                property_getter = o->get_getter(property_name);
                break;
            }
        }
        if (property_getter == nullptr)
            std::runtime_error{std::string{"no object named \""} + object_name + "\" found"};
        return (mo.get()->*property_getter)();
    }
    void add_a(pointer<A> new_A) {
        objects.push_back(new_A);
    }
private:
    std::vector<pointer<A>> objects;
};

struct testClass : A {
    int my_number() {
        return 8;
    }
    std::string get_name() override {
        return "testClass";
    }
    getter get_getter(std::string property_name) override  {
        if (property_name == "my_number") 
            return static_cast<A::getter>(my_number);
        throw std::runtime_error{std::string{"no property \""} + property_name + "\" found"};
    }       
};


int main() {
    PropertyManager p;
    p.add_a(std::make_shared<testClass>());
    std::cout << p.get_property("testClass","my_number");
}   

但是作为“属性”可以有任何其他类型,所以我觉得需要对代码进行模板化,但是由于不能对虚拟函数进行模板化,所以我不能,因此我显然无法通过此接口来实现,还有其他方法可以做到这一点吗,我的意思是说,有一个可以通过类似接口管理不同类的不同属性的类
我仅限于c ++ 11

1 个答案:

答案 0 :(得分:0)

好吧,我的命名方案有点笨,可以为您找到更好的东西,但是您可以尝试以下方法:

// initial base class we can return from A without having to worry about templates
class Getter
{
public:
    virtual ~Getter() { }
};

// intermediate class intended to be dynamic_casted to, see below
template<typename R>
class TheGetter : public Getter
{
public:
    virtual R operator()() = 0;
};

// final implementation
template<typename T, typename R>
class TheTheGetter : public TheGetter<R>
{
    T* t;
    R (T::*getter)();
public:
    TheTheGetter(T* t, R (T::*getter)())
            : t(t), getter(getter)
    { }

    R operator()() override
    {
        return (t->*getter)();
    }
};

struct A
{
    virtual ~A() { };
    virtual std::string get_name() = 0;

    // as virtual, cannot be a template, thus Getter cannot be either...
    virtual Getter& get_getter(std::string name) = 0;
};

// let's make a template function of:
template<typename T>
T PropertyManager::get_property(string_type object_name, string_type property_name)
{
    for(auto& o : objects)
    {
        if (o->get_name() == object_name)
        {
            auto& getter = o->get_getter(property_name);
            // now with the intermediate class, we do not have to care for
            // o's true type...
            auto* theGetter = dynamic_cast<TheGetter<T>*>(&getter);
            if(theGetter)
            {
                return (*theGetter)();
            }
            throw std::runtime_error("... is of bad type ...");
        }
    }
    throw std::runtime_error("...");
}

class Test : public A
{
public:
    Test()
            : gInt(this, &Test::getInt),
              gDouble(this, &Test::getDouble)
    { }

    int getInt() { return 7; }
    double getDouble() { return 10.12; }

    std::string get_name() override
    {
        return "Test";
    }

    Getter& get_getter(std::string name) override
    {
        if(name == "int")
        {
            return gInt;
        }
        if(name == "double")
        {
            return gDouble;
        }
        throw std::runtime_error("...");
    }

private:
    TheTheGetter<Test, int> gInt;
    TheTheGetter<Test, double> gDouble;
};

int main(int argc, char* argv[])
{
    PropertyManager p;
    p.add_a(std::make_shared<Test>());
    auto vi = p.get_property<int>("Test", "int");
    auto vd = p.get_property<double>("Test", "double");
    std::cout << vi << ' ' << vd << std::endl;
    return 0;
}