13

I have three functions I'm looking to merge together.

Each one takes an std::function as the first parameter, then executes it within a try/catch block.

The issue is, there are three different types of functions. Functions with no parameters, those with one integer parameter, and those with two integer parameters. The ones with integer parameters also have their corresponding parameters passed through the original function.

As one can see, each of the functions are nearly identical, so it would be nice if I could merge them all together. However, I'm unsure of anyway to setup a parameter that can receive any form of std::function and also rely on the fact it has been provided with corresponding data to use.

Here are the functions:

void run_callback(std::function<void()>& func) {
    try {
        func();
    } catch(const std::exception& ex) {
        print_callback_error(ex.what());
    } catch(const std::string& ex) {
        print_callback_error(ex.c_str());
    } catch(...) {
        print_callback_error();
    }
}

void run_callback_int(std::function<void(int)>& func, int data) {
    try {
        func(data);
    } catch(const std::exception& ex) {
        print_callback_error(ex.what());
    } catch(const std::string& ex) {
        print_callback_error(ex.c_str());
    } catch(...) {
        print_callback_error();
    }
}

void run_callback_intint(std::function<void(int, int)>& func, int data1, int data2) {
    try {
        func(data1, data2);
    } catch(const std::exception& ex) {
        print_callback_error(ex.what());
    } catch(const std::string& ex) {
        print_callback_error(ex.c_str());
    } catch(...) {
        print_callback_error();
    }
}

Any suggestions would be greatly appreciated!

max66
  • 65,235
  • 10
  • 71
  • 111
Griffort
  • 1,174
  • 1
  • 10
  • 26

2 Answers2

14

It seems to work with variadic templates.

Something like:

template <typename ... Args>
void run_callback(std::function<void(Args...)> const & func, Args ... as) {
    try {
        func(as...);
    } catch(const std::exception& ex) {
        print_callback_error(ex.what());
    } catch(const std::string& ex) {
        print_callback_error(ex.c_str());
    } catch(...) {
        print_callback_error();
    }
}

or (maybe better to manage possible forwarding)

template <typename ... FArgs, typename ... Args>
void run_callback(std::function<void(FArgs...)> const & func,
                  Args && ... as) {
    try {
        func(std::forward<Args>(as)...);
    } catch(const std::exception& ex) {
        print_callback_error(ex.what());
    } catch(const std::string& ex) {
        print_callback_error(ex.c_str());
    } catch(...) {
        print_callback_error();
    }
}
max66
  • 65,235
  • 10
  • 71
  • 111
  • 1
    I'll uptick this anyway, but you may want to add perfect forwarding to this. Just saying. – WhozCraig Feb 24 '18 at 21:22
  • @liliscent - D'Oh! Thanks! – max66 Feb 24 '18 at 21:24
  • @WhozCraig - you're right but I'm in doubt: how to write references for the type arguments of the `std::function`? – max66 Feb 24 '18 at 21:34
  • Absolute perfection, I knew there had to be some way! Do you have any recommendations on where I can learn about the "Args ..." stuff? (Sorry, I'm not certain on the correct terminology, but that stuff that letting you specify an indefinite amount of parameters and then referencing it in different ways within the function?) – Griffort Feb 24 '18 at 21:40
  • @Griffort They are called variadic templates. Googling them up brings up a lot of good references. – Arnav Borborah Feb 24 '18 at 21:43
  • 1
    @Griffort - the `Args...` stuff are "variadic templates"; it's a fundamental innovation of C++11 (they are not present in C++98). I don't know what suggest to learn they but if you find a good resource for C++11, do you find a good resource for variadic templates. – max66 Feb 24 '18 at 21:45
  • @WhozCraig - I've added another example to add forwarding but I've also added another variadic template list for `std::function`. It seems to me that, using perfect forwarding, can be a mismatch in the two types lists. What do you think? – max66 Feb 24 '18 at 21:51
  • @Griffort - following the WhozCraig's suggestion, I've added another example. It's a little more complicated but use another important C++11 feature: "perfect forwarding"; look also for "rvalues references", "lvalues references" and "move semantic". – max66 Feb 24 '18 at 21:54
  • @max66 Oh my, that looks even scarier. I'll try and see if I can come to learn it though. Safety aside, would one or the other be better for performance? Or are they generally going to be the same speed? – Griffort Feb 24 '18 at 22:07
  • 1
    @Grifford - in your case (a function that receive only simple integers) there isn't difference. More in general, the second form is a lot better of the first because permit the activation of move semantics, when appropriate. An the move semantics is a great performance improvement for C++11. – max66 Feb 24 '18 at 22:11
2

I suggest to use a lambda function:

void run_callback(std::function<void()>& func) {
    try {
        func();
    } catch(const std::exception& ex) {
        print_callback_error(ex.what());
    } catch(const std::string& ex) {
        print_callback_error(ex.c_str());
    } catch(...) {
        print_callback_error();
    }
}

To call this function you should:

run_callback([]{ func_without_params(); });

or

run_callback([&]{ func_with_1_param(a); });

And so on