带有不同参数的C ++ std :: function变量

时间:2015-09-28 12:30:52

标签: c++ c++11

在我的回调系统中,我想用不同的参数存储void Terrain::render(SDL_Renderer* renderer, Camera* camera) { int startTileXinCamera = camera->getCamera()->x / tileWidth; int startTileYinCamera = camera->getCamera()->y / tileHeight; int tileXinCamera = camera->cameraSizeToPositionX() / tileWidth; int tileYinCamera = camera->cameraSizeToPositionY() / tileHeight; for (int i = startTileYinCamera; i < tileYinCamera; i++) { for (int j = startTileXinCamera; j < tileXinCamera; j++) { terrainGraphic->render(renderer, terrain[i * tileCountX + j]->x - camera->getCamera()->x, terrain[i * tileCountX + j]->y - camera->getCamera()->y, tileClip[terrain[i * tileCountX + j]->type]); } } } (或其他东西)。

示例:

  1. 我想致电std::function
  2. 我想致电void()
  3. 我希望1)和2)存储在同一个变量中并选择在执行调用中调用的内容

    void(int, int)

    有可能做这样的事吗?或者我必须创建几个&#34; FuntionPointer&#34;基于输入参数计数的模板。

    修改

    是否有可能以某种方式利用std :: bind来完成这项任务?使用std :: bind,我可以FunctionPointer f0; FunctionPointer f2; f0(); f2(4, 5);

    编辑2

    实际用例:我有一个触发系统,我想为动作指定功能指针,所以当动作发生时,会调用函数。 伪代码样本:

    std::function<void()> f = std::bind(test, 2, 5);

    或如果可能的话

    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)
    

6 个答案:

答案 0 :(得分:10)

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用于相同目的。

答案 1 :(得分:4)

以下解决方案可能对您有用(我不确定这里的代码是绝对正确的):

使用虚拟析构函数为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)...);
    }
};

答案 2 :(得分:4)

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

答案 3 :(得分:2)

好吧,如果您可以使用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 (*)()值存储不同的函数原型。需要时,使用该地图检索正确的函数。

Here is the working example

答案 4 :(得分:1)

如果您不需要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。

答案 5 :(得分:0)

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

它是一个small header,你可以用它来伸长&#34;功能签名。 这允许您执行此操作(从github example中提取):

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