非常具体的子类方法调用

Very specific sub-class method call

本文关键字:类方法 调用 非常      更新时间:2023-10-16

我有这样的设计:

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只是例子)
  • I 必须能够有一个CompBase
  • 的集合
  • 计算函数必须不必须检查它们的输入(即传递相同的结构是不可能的)
  • 代码必须足够通用,以便轻松添加其他数据,比较和计算

下面是一个使用的例子:

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;
}

我这里有一个"工作"的例子。

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

在这些约束条件下是可能的吗?

谢谢大家的回答。@Daniel Jour,你的帖子真的帮助了我,我有一些修改来适应我的情况。

这是一个更新的例子,将为我工作。

#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;
}