Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ std::function variable with varying arguments

Tags:

c++

c++11

In my callback system I want to store std::function (or something else) with varying arguments.

Example:

  1. I want to call void()
  2. I want to call void(int, int)

I want 1) and 2) to be stored in the same variable and choose what to call in actuall call

FunctionPointer f0;
FunctionPointer f2;

f0();
f2(4, 5);

Is it possible to do something like this? Or I have to create several "FuntionPointer" templates based on input arguments count.

EDIT

Is it possible to utilize std::bind somehow for this task? With std::bind I can have std::function<void()> f = std::bind(test, 2, 5);

EDIT 2

Practical use case: I have a trigger system and I want to assign funtion pointers to actions, so when action happen, function is called. Pseudo-code sample:

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)

or if possible

Input:
  Find Trigger by input
  if (in == A) f = bind(Trigger.f);
  else f = bind(Trigger.f, 10, 20);
  f()
like image 642
Martin Perry Avatar asked Sep 28 '15 12:09

Martin Perry


People also ask

How do you write a function with variable number of arguments in C++?

Variable number of arguments in C++Define a function with its last parameter as ellipses and the one just before the ellipses is always an int which will represent the number of arguments. Create a va_list type variable in the function definition. This type is defined in stdarg. h header file.

What is variadic function C++?

Variadic functions are functions (e.g. printf) which take a variable number of arguments. The declaration of a variadic function uses an ellipsis as the last parameter, e.g. int printf(const char* format, ...);. See variadic arguments for additional detail on the syntax and automatic argument conversions.

Can variables be used as function arguments?

No, you will be unable to change the global variable used as an argument of a function parameter. The function parameter gets a copy of the global variable. It itself (the parameter) is a local variable of the function.

Which function accepts a variable number of arguments?

In mathematics and in computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments.


2 Answers

The following solution might work for you (I'm not sure that the code is absolutely correct here):

Create a wrapper for std::function with virtual destructor to enable using dynamic cast

class function_wrapper_base
{
    virtual ~function_wrapper_base();
}

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

Then create a class 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)...);
    }
};
like image 20
Andrey Nasonov Avatar answered Oct 17 '22 23:10

Andrey Nasonov


std::function<void()> and std::function<void(int, int)> are two absolutely distinct types. You need some sort of union functionality (or polymorphism) to store an object of an unknown type.

If you can use Boost, you could easily do this with 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);
}

It is up to you to provide the logic of fContainsNullary(). Alternatively, you can use the variant's own stored knowledge of value type by using a visitor:

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

If Boost is not an option, you can hand-craft a suitable union for much the same purpose.

like image 74
Angew is no longer proud of SO Avatar answered Oct 18 '22 00:10

Angew is no longer proud of SO