非常具体的子类方法调用

时间:2015-09-04 19:34:58

标签: c++ c++11

我有这个设计:

class GenericData
{
};

class Data1 : public GenericData
{
};

class Data2 : public GenericData
{
};

class CompBase  
{
public:
    void process()
    {
        // inputs are check to make sure there number and order is correct
        // Use them to automatically call the correct function
        // What here ?
    }
    vector<GenericData> inputs;
};

class Comp1 : public CompBase
{
public:
    void compute(Data1 input1, Data1 input2) { cout << "Comp1::compute(Data1 input1, Data1 input2)" << endl; }
    void compute(Data2 input1, Data2 input2) { cout << "Comp1::compute(Data2 input1, Data2 input2)" << endl; }
    void compute(Data1 input1, Data2 input2) { cout << "Comp1::compute(Data1 input1, Data2 input2)" << endl; }
};

class Comp2 : public CompBase
{
public:
    void compute(Data1 input1) { cout << "Comp2::compute(Data1 input1)" << endl; }
    void compute(Data2 input1) { cout << "Comp2::compute(Data2 input1)" << endl; }
};

具有以下约束条件:

  • 计算函数必须从GenericComp调用,但不能在此声明,因为会有两个(Data1,2和Comp1,2只是示例)
  • 必须才能拥有CompBase
  • 的集合
  • 计算功能必须必须检查其输入(即无法传递相同的结构)
  • 代码必须足够通用,以便轻松添加其他数据,Comp和计算

以下是使用示例:

int main() {
    Data1 d1;   Data2 d2;
    Comp1 c1; Comp2 c2;

    c1.inputs = { d1, d1 };
    c1.process();           // "Comp1::compute(Data1 input1, Data1 input2)"

    c1.inputs = { d2, d2 };
    c1.process();           // "Comp1::compute(Data2 input1, Data2 input2)"

    c1.inputs = { d1, d2 };
    c1.process();           // "Comp1::compute(Data1 input1, Data2 input2)"

    vector<GenericComp> comps = { c1, c2 };
    for (comp : comps)
    {
        comp.process();
    }
    return 0;
}

我有一个here的“工作”示例。

我尝试了不同的方法:CRTP,可变参数模板函数,currying和部分应用以及大量的谷歌搜索,但我被困在这里。

这些限制是否可能?如果是这样你怎么能这样做?

1 个答案:

答案 0 :(得分:0)

谢谢你们的回答。 @Daniel Jour,你的帖子真的对我很有帮助,而且我做了一些修改以适应我的情况。

Here是一个适合我的更新示例。

#include <iostream>
#include <vector>
#include <map>
#include <functional>
#include <memory>
using namespace std;

class GenericData
{
public:
    virtual ~GenericData() {};
};

class Data1 : public GenericData
{
public:
    virtual ~Data1() {};
};

class Data2 : public GenericData
{
public:
    virtual ~Data2() {};
};

class GenericComp
{
public:
    virtual ~GenericComp() {};
    vector<GenericData*> inputs;
};

class Comp1 : public GenericComp
{
public:
    static bool compute(shared_ptr<Data1> const & input1, shared_ptr<Data1> const & input2) { cout << "Comp1::compute(Data1 input1, Data1 input2)" << (input2 ? "ok" : "null") << endl; return true; }
    static bool compute(shared_ptr<Data2> const & input1, shared_ptr<Data2> const & input2) { cout << "Comp1::compute(Data2 input1, Data2 input2)" << endl; return true; }
    static bool compute(shared_ptr<Data1> const & input1, shared_ptr<Data2> const & input2) { cout << "Comp1::compute(Data1 input1, Data2 input2)" << endl; return true; }
};

class Comp2 : public GenericComp
{
public:
    static bool compute(shared_ptr<Data1> const & input1) { cout << "Comp2::compute(Data1 input1)" << endl; return true; }
    static bool compute(shared_ptr<Data2> const & input1) { cout << "Comp2::compute(Data2 input1)" << endl; return true; }
};

// Arguments type to the function "interface"
using Arguments = std::vector<shared_ptr<GenericData>> const &;
// the interface
using Function = std::function<bool (Arguments)>;

// Base case of packing a function.
// If it's taking a vector and no more
// arguments, then there's nothing left to
// pack.
template<std::size_t N, typename Fn>
Function pack(Fn && fn)
{
    return [fn = std::forward<decltype(fn)>(fn)] (Arguments arguments)
    {
        if (N != arguments.size())
        {
            throw std::string{"wrong number of arguments, expected "} +
              std::to_string(N) +
              std::string{" but got "} +
              std::to_string(arguments.size());
        }
        return fn(arguments);
    };
}

// pack a function to a function that takes
// it's arguments from a vector, one argument after
// the other.
template<std::size_t N, typename Arg, typename... Args, typename Fn>
Function pack(Fn && fn)
{
    return pack<N+1, Args...>([fn = std::forward<decltype(fn)>(fn)] (Arguments arguments, Args const &... args)
    {
        try
        {
            return fn(arguments, arguments.at(N), args...);
        }
        catch (std::bad_cast const &)
        {
            throw std::string{"argument "} + std::to_string(N) + std::string{" has wrong type "};
        }
    });
}

// transform a function into one that takes its
// arguments from a vector
template<typename... Args, typename Fn>
Function pack_function(Fn && fn)
{
    return pack<0, Args...>([fn = std::forward<decltype(fn)>(fn)] (Arguments arguments, Args const &... args) -> bool
    {
        return fn(args...);
    });
}

int main() {
    // Pack all the functions
    std::map<std::string, Function> operations;
    operations["Comp1_Data1_Data1"] = pack_function<shared_ptr<GenericData>, shared_ptr<GenericData>>([] (shared_ptr<GenericData> const & i1, shared_ptr<GenericData> const & i2)
    {
        return Comp1::compute(dynamic_pointer_cast<Data1>(i1), dynamic_pointer_cast<Data1>(i2));
    });
    operations["Comp1_Data2_Data2"] = pack_function<shared_ptr<GenericData>, shared_ptr<GenericData>>([] (shared_ptr<GenericData> const & i1, shared_ptr<GenericData> const & i2)
    {
        return Comp1::compute(dynamic_pointer_cast<Data2>(i1), dynamic_pointer_cast<Data2>(i2));
    });
    operations["Comp1_Data1_Data2"] = pack_function<shared_ptr<GenericData>, shared_ptr<GenericData>>([] (shared_ptr<GenericData> const & i1, shared_ptr<GenericData> const & i2)
    {
        return Comp1::compute(dynamic_pointer_cast<Data1>(i1), dynamic_pointer_cast<Data2>(i2));
    });
    operations["Comp2_Data1"] = pack_function<shared_ptr<GenericData>>([] (shared_ptr<GenericData> const & i1)
    {
        return Comp2::compute(dynamic_pointer_cast<Data1>(i1));
    });
    operations["Comp2_Data2"] = pack_function<shared_ptr<GenericData>>([] (shared_ptr<GenericData> const & i1)
    {
        return Comp2::compute(dynamic_pointer_cast<Data2>(i1));
    });

    // Create the possible inputs
    vector<shared_ptr<GenericData>> data1_data1 { shared_ptr<Data1>(), shared_ptr<Data1>() };
    vector<shared_ptr<GenericData>> data2_data2 { shared_ptr<Data2>(), shared_ptr<Data2>() };
    vector<shared_ptr<GenericData>> data1_data2 { shared_ptr<Data1>(), shared_ptr<Data2>() };
    vector<shared_ptr<GenericData>> data1 { shared_ptr<Data1>() };
    vector<shared_ptr<GenericData>> data2 { shared_ptr<Data2>() };

    // The calls !
    operations["Comp1_Data1_Data1"](data1_data1);
    operations["Comp1_Data2_Data2"](data2_data2);
    operations["Comp1_Data1_Data2"](data1_data2);
    operations["Comp2_Data1"](data1);
    operations["Comp2_Data2"](data2);

    // Wrong arguments
    try
    {
        operations["Comp1_Data1_Data1"](data1);
    }
    catch (std::string const & e)
    {
        cout << e << endl;
    }
    try
    {
        operations["Comp2_Data1"](data1_data1);
    }
    catch (std::string const & e)
    {
        cout << e << endl;
    }
    return 0;
}