0

What I'm trying to achieve is creating a struct which stores any kind of method. I can later call struct_object.run() to run the method I've stored.

This method can return any kind of value and, most importantly, use any amount of parameters; however, I can't get around the "any amount of parameters" issue.

Mind you, the following code doesn't even build, mostly because I have no clue on what the correct syntax would be like.

ApplicationPair.h

template<typename T, typename... Args>
struct ApplicationPair
{
    ApplicationPair(boost::function<T()> func, Args... arguments )
    {
        _func = func(Args::arguments...);
    }

    ApplicationPair() = delete;

    void run(); 
    boost::function<T(Args...)> _func;
};

#endif

And then, what I'd like to do is the following:

main.cpp

template<typename T, typename... Args>
void ApplicationPair<T,Args...>::run()
{
    this->_func;
}

//TEST

int counter = 0;

void HelloWorld()
{
    std::cout << "HelloWorld\n";
}

void printNumber(int i)
{
    std::cout << "Print: " << i << std::endl;
}

void increaseCounter(int x)
{
   counter+=x;
}

int main()
{
    ApplicationPair<void> p1(HelloWorld);
    ApplicationPair<void> p2(printNumber, 5);
    ApplicationPair<void> p3(increaseCounter, 10);
    p1.run();
    p2.run();
    p3.run();
    return 0;
}

Basically, the methods I want to store shouldn't be modified or adapted in any way: I want to be able to create any kind of method without caring about the fact that struct ApplicationPair will store it for its own personal use.

All I get with this though is a long string of errors like:

error: in declaration ‘typename boost::enable_if_c<(! boost::is_integral::value), boost::function&>::type boost::function::operator=(Functor)’

someCoder
  • 185
  • 3
  • 15
  • 1
    Unrelated: If you are using C++11 or later, you can use [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function) instead of `boost::function`. – Ted Lyngmo Apr 20 '20 at 08:42
  • @TedLyngmo yeah I'm just using boost::function because that's what I'm asked to use at work. – someCoder Apr 20 '20 at 08:47
  • how are you planning to call the functions when they can have any number of paramters? And how do you want to use the returned values? `this->_func;` does nothing, you have to actually call the function – 463035818_is_not_an_ai Apr 20 '20 at 08:48
  • the parameters are already passed on construction? In that case you only need to store functions that take no parameters and bind the parameters – 463035818_is_not_an_ai Apr 20 '20 at 08:51
  • 1
    Is there a particular need for the name `run`? If you changed it to `operator()`, you can use `boost::function` directly – Caleth Apr 20 '20 at 10:09

2 Answers2

2

In the below line:

ApplicationPair<void> p2(printNumber, 5);

you have to specify all types in template arguments list, not only void as return type, int as argument of constructor should also be added. Now args... is empty. What is wrong. The same with p3.

Make constructor as templated method taking paramters pack as argument for your callable:

template<class F, class ... Args>
ApplicationPair(F&& func, Args... arguments )
{
    _func = boost::bind(std::forward<F>(func),arguments...);
}

then args... can be deduced when invoking constructor. Your class template takes only a type for return value.

template<class Ret>
struct ApplicationPair {
    template<class F, class ... Args>
    ApplicationPair(F&& func, Args... arguments )
    {
        _func = boost::bind(std::forward<F>(func),arguments...);
    }
    ApplicationPair() = delete;
    void run() {
        this->_func();
    }
    boost::function<Ret()> _func;
};

In constructor boost::bind is used to bind passed parameters to callable. You don't store parameters anywhere, therefore they must be bound in functor created by boost::bind.

Uses:

ApplicationPair<void> p1(HelloWorld);
ApplicationPair<void> p2(printNumber, 5);
ApplicationPair<void> p3(increaseCounter, 10);

Demo


Don't use boost::bind, it is limited to handle only max 9 arguments.

rafix07
  • 20,001
  • 3
  • 20
  • 33
  • This is perfect, exactly what I was looking for. I'll have to study the code a bit to understand how everything works exactly (like the fact you're using std::forward, which I've unfortunately never used before) but now I finally have a correctly working code.THANK YOU! – someCoder Apr 20 '20 at 09:38
  • `std::forward` maintains the value category of the parameter – Caleth Apr 20 '20 at 10:10
1

You've already gotten an answer but here's a C++17 alternative capable of deducing the return value type as well as the argument types of the function using a deduction guide, making both the return type and argument types part of the ApplicationPair<> type. I've chosen to store the arguments separately in a std::tuple<Args...>.

boost::function can be replaced with std::function in this example in case you later decide to go with the standard:

#include <boost/function.hpp>
#include <iostream>
#include <type_traits>
#include <tuple>

template<typename T, typename... Args>
struct ApplicationPair {
    ApplicationPair() = delete;

    ApplicationPair(Func func, Args... args) : 
        _func(func),
        // store the arguments for later use
        arguments(std::make_tuple(std::forward<Args>(args)...))
    {}

    decltype(auto) run() {        // I'd rename this: decltype(auto) operator()()
        return std::apply(_func, arguments);
    }

    boost::function<T(Args...)> _func;
    std::tuple<Args...> arguments;
};

// deduction guide
template<typename Func, typename... Args>
ApplicationPair(Func, Args...) -> 
    ApplicationPair<std::invoke_result_t<Func, Args...>, Args...>;
int counter = 0;

void HelloWorld()
{
    std::cout << "HelloWorld\n";
}

void printNumber(int i)
{
    std::cout << "Print: " << i << std::endl;
}

int increaseCounter(int x) // changed return type for demo
{
   counter+=x;
   return counter;
}

int main()
{
    // full deduction using the deduction guide
    ApplicationPair p1(HelloWorld);
    ApplicationPair p2(printNumber, 5);
    ApplicationPair p3(increaseCounter, 10);
    p1.run();
    p2.run();
    std::cout << p3.run() << '\n';
    std::cout << p3.run() << '\n';
}
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108