带有不同参数的c++ std::函数变量

C++ std::function variable with varying arguments

本文关键字:std 函数 变量 c++ 参数      更新时间:2023-10-16

在我的回调系统中,我想用不同的参数存储std::function(或其他东西)。

的例子:

  1. 我想呼叫void()
  2. 我想呼叫void(int, int)

我希望1)和2)存储在同一个变量中,并选择实际调用

FunctionPointer f0;
FunctionPointer f2;
f0();
f2(4, 5);

有可能这样做吗?或者我必须根据输入参数计数创建几个"functionpointer"模板。

编辑

是否可以利用std::bind来完成这个任务?使用std::bind,我可以有std::function<void()> f = std::bind(test, 2, 5);

编辑2

实际用例:我有一个触发器系统,我想给动作分配函数指针,所以当动作发生时,函数被调用。伪代码示例:

structure Trigger 
{
   Function f;
}
Init:
  Trigger0.f = pointer to some function ()
  Trigger1.f = pointer to some function (a, b)
Input: 
  Find Trigger by input
  if (in == A) Trigger.f();
  else Trigger.f(10, 20)

或者

Input:
  Find Trigger by input
  if (in == A) f = bind(Trigger.f);
  else f = bind(Trigger.f, 10, 20);
  f()

std::function<void()>std::function<void(int, int)>是两种完全不同的类型。您需要某种联合功能(或多态性)来存储未知类型的对象。

如果您可以使用Boost,您可以轻松地使用boost::variant:

// Declaration:
boost::variant<std::function<void()>, std::function<void(int, int)> > f;
// Calling, explicit:
if (fContainsNullary()) {
  boost::get<std::function<void()>>(f)();
} else {
  boost::get<std::function<void(int, int)>>(f)(4, 5);
}

由您来提供fContainsNullary()的逻辑。或者,您可以通过使用访问器来使用变量自己存储的值类型知识:

struct MyVisitor : boost::static_visitor<void>
{
  result_type operator() (const std::function<void()> &a) {
    a();
  }
  result_type operator() (const std::function<void(int, int)> &a) {
    a(4, 5);
  }
};
// Calling, via visitor:
boost::apply_visitor(MyVisitor(), f);

如果Boost不是一个选项,你可以手工制作一个合适的union来达到同样的目的。

下面的解决方案可能适合您(我不确定这里的代码是否绝对正确):

std::function创建一个带有虚拟析构函数的包装器,以启用动态强制转换

class function_wrapper_base
{
    virtual ~function_wrapper_base();
}
template <class... Args>
class function_wrapper
    : public function_wrapper_base
{
public:
   std::function<void, Args...> f;
   ...
};

创建一个类variant_function_holder

class variant_function_holder
{
    std::unique_ptr<function_wrapper_base> f;
    ...
    template <class... Args>
    void operator()(Args&&... args)
    {
        function_wrapper<std::decay<Args>::type...> * g = dynamic_cast<function_wrapper<std::decay<Args>::type...>>(f.get());
        if (g == nullptr)
        {
            // ToDo
        }
        g->f(std::forward<Args>(args)...);
    }
};

嗯,如果你可以使用RTTI,你可以这样定义一个MultiFuncObject,你可以很容易地绑定其他函数。而且,你可以很容易地打电话给他们。但不幸的是,这种方法只适用于有限数量的参数。但实际上boost::bind也支持有限数量的参数(默认为9),所以你可以扩展这个类来满足你的需要。

在给你MultiFuncObject的源代码之前,我想告诉你如何使用它。它接受一个模板参数作为返回类型。可以使用+=操作符绑定新函数。通过一些模板魔法,类可以区分具有相同实参计数但至少有一个不同实参类型的绑定函数。

你需要c++ 11,因为MultiFuncObject使用std::unordered_mapstd::type_index

用法:

#include <iostream>
using namespace std;
void _1() {
  cout << "_1" << endl;
}
void _2(char x) {
  cout << "_2" << " " << x << endl;
}
void _3(int x) {
  cout << "_3" << " " << x << endl;
}
void _4(double x) {
  cout << "_4" << " " << x << endl;
}
void _5(int a, int b) {
  cout << "_5" << " " << a << " " << b << endl;
}
void _6(char a, int b) {
  cout << "_6" << " " << a << " " << b << endl;
}
void _7(int a, int b, int c) {
  cout << "_7" << " " << a << " " << b << " " << c << endl;
}
int main() {
  MultiFuncObject<void> funcs;
  funcs += &_1;
  funcs += &_2;
  funcs += &_3;
  funcs += &_4;
  funcs += &_5;
  funcs += &_6;
  funcs += &_7;
  funcs();
  funcs('a');
  funcs(56);
  funcs(5.5);
  funcs(2, 5);
  funcs('q', 6);
  funcs(1, 2, 3);
  return 0;
}

我希望这是接近你想要的。以下是MultiFuncObject的来源:

#include <typeinfo>
#include <typeindex>
#include <unordered_map>
using namespace std;
template <typename R>
class MultiFuncObject {
  unordered_map<type_index, void (*)()> m_funcs;
public:
  MultiFuncObject<R> operator +=( R (* f)() ) {
    m_funcs[typeid( R() )] = (void (*)()) f;
    return *this;
  }
  template <typename A1>
  MultiFuncObject<R> operator +=( R (* f)(A1) ) {
    m_funcs[typeid( R(A1) )] = (void (*)()) f;
    return *this;
  }
  template <typename A1, typename A2>
  MultiFuncObject<R> operator +=( R (* f)(A1, A2) ) {
    m_funcs[typeid( R(A1, A2) )] = (void (*)()) f;
    return *this;
  }
  template <typename A1, typename A2, typename A3>
  MultiFuncObject<R> operator +=( R (* f)(A1, A2, A3) ) {
    m_funcs[typeid( R(A1, A2, A3) )] = (void (*)()) f;
    return *this;
  }
  R operator()() const
  {
    unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R() ));
    if (it != m_funcs.end()) {
      R (*f)() = ( R (*)() )(it->second);
      (*f)();
    }
  }
  template <typename A1>
  R operator()(A1 a1) const
  {
    unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R(A1) ));
    if (it != m_funcs.end()) {
      R (*f)(A1) = ( R (*)(A1) )(it->second);
      (*f)(a1);
    }
  }
  template <typename A1, typename A2>
  R operator()(A1 a1, A2 a2) const
  {
    unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R(A1, A2) ));
    if (it != m_funcs.end()) {
      R (*f)(A1, A2) = ( R (*)(A1, A2) )(it->second);
      (*f)(a1, a2);
    }
  }
  template <typename A1, typename A2, typename A3>
  R operator()(A1 a1, A2 a2, A3 a3) const
  {
    unordered_map<type_index, void (*)()>::const_iterator it = m_funcs.find(typeid( R(A1, A2, A3) ));
    if (it != m_funcs.end()) {
      R (*f)(A1, A2, A3) = ( R (*)(A1, A2, A3) )(it->second);
      (*f)(a1, a2, a3);
    }
  }
};

使用std::unordered_map存储不同的函数原型,键为std::type_index,值为void (*)()。当需要时,使用该映射检索正确的函数。

下面是工作示例

c++ 11救急!

如果你可以将你的函数泛化为不带参数的函数对象,那么你就可以用任意lambda调用它。

#include <iostream>
using namespace std;
template <class F>
void call_it(F&& f)
{
    f();
}
int main()
{
    int x = 50, y = 75;
    call_it([] () { cout << "Hello!n"; });
    call_it([x,y] () { cout << x << " + " << y << " = " << x + y << 'n';});

    return 0;
}

如果您不需要std::function,您可以创建一个代理类。

class fn_t {
public:
  typedef void (*fn_1_t)();
  typedef void (*fn_2_t)(int, int);
  fn_1_t fn_1;
  fn_2_t fn_2;
  fn_t operator=(fn_1_t func_1) { fn_1 = func_1; return *this; }
  fn_t operator=(fn_2_t func_2) { fn_2 = func_2; return *this; }
  void operator()() { (*fn_1)(); }
  void operator()(int a, int b) { (*fn_2)(a, b); }
};
#include <iostream>
using namespace std;
void first() {
  cout << "first" << endl;
}
void second(int a, int b) {
  cout << "second " << a << " : " << b << endl;
}
int main() {
  fn_t f;
  f = &first;
  f = &second;
  f();
  f(5, 4);
  return 0;
}

fn_t可以自动处理你想要的两个原型,自动分配需要的一个原型,并且可以通过给()操作符重载适当的参数来调用两个原型的函数。

你可能想检查函数指针fn_1fn_2的有效性,但我没有包括这个最小性检查。

这样做的好处是你只需要c++,甚至不需要STL和Boost。

其他答案都很好,但我也想展示我的解决方案。

这是一个小的头文件,你可以用它"拉长"函数签名。这允许您这样做(从github示例中提取):

int foo_1p(int a);
int foo_2p(int a, int b);
int foo_3p(int a, int b, int c);
int foo_4p(int a, int b, int c, int d);
int foo_5p(int a, int b, int c, int d, int e);
int foo_6p(int a, int b, int c, int d, int e, int f);
int foo_7p(int a, int b, int c, int d, int e, int f, std::string g);
...
int main()
{
   std::unordered_map<std::string, std::function<int(int, int, int, int, int, int, std::string)>> map;
   map["foo_1p"] = ex::bind(foo_1p, ph, ph, ph, ph, ph, ph);
   map["foo_2p"] = ex::bind(foo_2p, ph, ph, ph, ph, ph);
   map["foo_3p"] = ex::bind(foo_3p, ph, ph, ph, ph);
   map["foo_4p"] = ex::bind(foo_4p, ph, ph, ph);
   map["foo_5p"] = ex::bind(foo_5p, ph, ph);
   map["foo_6p"] = ex::bind(foo_6p, ph);
   map["foo_7p"] = foo_7p;
   for (const auto& f : map)
   {
       std::cout << f.first << " = " << f.second(1, 1, 1, 1, 1, 1, "101") << std::endl;
   }
}