有没有办法将具有任何类型和数量的参数的 void 函数作为参数传递给方法并将其存储在数据成员中? (C++)

Is there a way to pass a void function with any type and amount of arguments as an argument in a method and store it in a data member? (C++)

我 运行 在将函数作为参数传递到方法中时遇到问题。我的问题是我希望能够传递具有任何类型和数量的参数的任何 void 函数,然后将其存储在数据成员中。我目前所做的是对不带参数的 void 函数进行重载,并对具有 std::any 类型向量参数的 void 函数进行重载,然后为该向量添加一个额外的参数。这是我所做的一个例子:

typedef void (*FunctionT1)();
typedef void (*FunctionT2)(std::vector<std::any>);

class ExampleClass {
public:
    void saveFunction(FunctionT1 f) {
        emptyFunction = f;
    }
    void saveFunction(FunctionT2 f, std::vector<std::any> args) {
        vectorFunction = f;
        vectorFunctionArgs = args;
    }
private:
    FunctionT1 emptyFunction;

    FunctionT2 vecotorFunction;
    std::vector<std::any> vectorFunctionArgs;
}

现在这可行,我可以做我想做的事,但这显然是一个非常糟糕的解决方案。有没有办法以其他方式做到这一点? (我在考虑模板包,但无法弄清楚它们是如何工作的)

我想向您展示一个抽象工厂的例子。

对于工厂模式,我们通常会遇到问题,即所有构造函数都应该具有相同的签名。但是我们经常希望使用具有不同参数集的函数。

如果您查看下面的示例,我可以向工厂添加具有任意数量参数的函数。

我知道这不是您想要的,但它可能会提示您如何实现自己的功能。

#include <iostream>
#include <map>
#include <utility>
#include <any>


// Some demo classes ----------------------------------------------------------------------------------
struct Base {
    Base(int d) : data(d) {};
    virtual ~Base() { std::cout << "Destructor Base\n"; }
    virtual void print() { std::cout << "Print Base\n"; }
    int data{};
};
struct Child1 : public Base {
    Child1(int d, std::string s) : Base(d) { std::cout << "Constructor Child1 " << d << " " << s << "\n"; }
    virtual ~Child1() { std::cout << "Destructor Child1\n"; }
    virtual void print() { std::cout << "Print Child1: " << data << "\n"; }
};
struct Child2 : public Base {
    Child2(int d, char c, long l) : Base(d) { std::cout << "Constructor Child2 " << d << " " << c << " " << l << "\n"; }
    virtual ~Child2() { std::cout << "Destructor Child2\n"; }
    virtual void print() { std::cout << "Print Child2: " << data << "\n"; }
};
struct Child3 : public Base {
    Child3(int d, long l, char c, std::string s) : Base(d) { std::cout << "Constructor Child3 " << d << " " << l << " " << c << " " << s << "\n"; }
    virtual ~Child3() { std::cout << "Destructor Child3\n"; }
    virtual void print() { std::cout << "Print Child3: " << data << "\n"; }
};



using UPTRB = std::unique_ptr<Base>;


template <class Child, typename ...Args>
UPTRB createClass(Args...args) { return std::make_unique<Child>(args...); }

// The Factory ----------------------------------------------------------------------------------------
template <class Key, class Object>
class Factory
{
    std::map<Key, std::any> selector;
public:
    Factory() : selector() {}
    Factory(std::initializer_list<std::pair<const Key, std::any>> il) : selector(il) {}

    template<typename Function>
    void add(Key key, Function&& someFunction) { selector[key] = std::any(someFunction); };

    template <typename ... Args>
    Object create(Key key, Args ... args) {
        if (selector.find(key) != selector.end()) {
            return std::any_cast<std::add_pointer_t<Object(Args ...)>>(selector[key])(args...);
        }
        else return nullptr;
    }
};

int main()
{
    Factory<int, UPTRB> factory{
        {1, createClass<Child1, int, std::string>},
        {2, createClass<Child2, int, char, long>}
    };
    factory.add(3, createClass<Child3, int, long, char, std::string>);


    // Some test values
    std::string s1(" Hello1 "); std::string s3(" Hello3 ");
    int i = 1;  const int ci = 1;   int& ri = i;    const int& cri = i;   int&& rri = 1;

    UPTRB b1 = factory.create(1, 1, s1);
    UPTRB b2 = factory.create(2, 2, '2', 2L);
    UPTRB b3 = factory.create(3, 3, 3L, '3', s3);

    b1->print();
    b2->print();
    b3->print();
    b1 = factory.create(2, 4, '4', 4L);
    b1->print();
    return 0;
}

通过阅读问题和浏览评论,我认为这是最适合您的问题的,

#include <vector>

class ExampleClass {
public:
    ExampleClass() = default;

    /**
     * Save a function pointer. 
     * 
     * @param Function: The function poitner.
     * @return The index of the function.
     */
    template<class Return = void, class... Args>
    size_t SaveFunction(Return(*Function)(Args... args))
    {
        mFunctions.insert(mFunctions.end(), Function);
        return mFunctions.size() - 1;
    }

    /**
     * Call a saved function.
     * 
     * @tparam Return: The return type of the function.
     * @param index: The index of the function.
     * @param args: The arguments the function requires.
     * @return The return of the function.
     */
    template<class Return = void, class... Args>
    Return CallFunction(size_t index, const Args&... args)
    {
        typedef Return(*Function)(Args...);

        return reinterpret_cast<Function>(mFunctions[index])(args...);
    }

private:
    std::vector<void*> mFunctions;
};

然后你就可以这样保存和调用任何函数了,

void Function() { std::cout << "Hello World\n"; }
void Add(int x, int y) { std::cout << x + y << std::endl; }

int main()
{
    ExampleClass c;
    size_t f1 = c.SaveFunction(Function);
    size_t f2 = c.SaveFunction(Add);

    c.CallFunction(f1);
    c.CallFunction(f2, 1, 2);
}

这适用于具有任意数量参数的任何函数。

注意:如果 return 类型总是 void,您可以将 Return 替换为 void

据我了解,您想调用一个函数 void(),其中调用者可能会使用绑定参数注册其他函数类型。您可以使用 std::function:

class ExampleClass {
public:
    void saveFunction(std::function<void()> f) {
        this->f = f;
    }
    void call() { f(); }
private:
    std::function<void()> f;
};

有使用

void foo();
void bar(std::vector<std::any>);

std::vector<std::any> v;

ExampleClass ex;

ex.SaveFunction(&foo); ex.call(); // foo();
ex.SaveFunction([&]() { bar(v); }); ex.call(); // bar(v);
// You might capture by copy/move if you prefer (instead of by reference)