Hunter Mueller Hunter Mueller - 3 months ago 25
C++ Question

Alternative to std::function for passing function as argument (callbacks, etc.)

I stumbled across this during my experiments with C++11. I find that it is an obvious solution, but I haven't been able to find any other examples of it in the wild, so I'm concerned that there's something I'm missing.

The practice I'm referring to (in the "addAsync" function):

#include <thread>
#include <future>
#include <iostream>
#include <chrono>

int addTwoNumbers(int a, int b) {
std::cout << "Thread ID: " << std::this_thread::get_id() << std::endl;

return a + b;
}

void printNum(std::future<int> future) {
std::cout << future.get() << std::endl;
}

void addAsync(int a, int b, auto callback(std::future<int>) -> void) { //<- the notation in question
auto res = std::async(std::launch::async, addTwoNumbers, a, b);

if (callback) //super straightforward nullptr handling
return callback(std::move(res));
}

int main(int argc, char** argv) {
addAsync(10, 10, [](std::future<int> number) { //lambda functions work great
addAsync(number.get(), 20, [](std::future<int> number) {
addAsync(893, 4387, printNum); //as do standard functions
addAsync(2342, 342, nullptr); //executes, sans callback

std::cout << number.get() << std::endl;
});
});

std::cout << "main thread: " << std::this_thread::get_id() << std::endl;

return 0;
}


Is it considered bad practice, or is it non-portable (I've only tried it in MSVC++ 2015)? Also, how does the compiler treat this; by conversion to std::function?

I would love to keep using this in my projects, as it obviously states the required argument types and return type in the "signature", accepts a nullptr for optionality, and seems to "just work" (I am aware that these are famous last words in C++).

Answer

You are using a raw pointer to function.

Unlike std::function, this will not work with a lambda that captures, or with a result of std::bind, or with a general class type that implements operator().