Avi Kivity
unread,Dec 9, 2016, 9:19:42 AM12/9/16Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to ISO C++ Standard - Future Proposals
Often, one wants to call a function with a one-off callback. There are
currently three ways to do this:
1. templates
template <typename Func> // void (int, int)
void foo(int a, int b, Func func);
The problem here is that foo's definition must be available, and there
is an increase in compile time, executable size, and the odds of seeing
a cryptic error (to be reduced with concepts).
2. std::function
void foo(int a, int b, std::function<void (int, int)> func);
We've solved the problems of the template above, but added a virtual
function call, an allocation (which can be avoided be the
implementation, sometimes), and a requirement that the lambda we send
here be copyable; if we pass func down the line, we add either extra
indirection or extra copies/allocations.
3. custom abstract class
class foo_callback {
public:
virtual ~foo_callback() {} // not really necessary
virtual void call(int, int) const = 0;
};
void foo(int a, int b, const foo_callback& func);
This is the C++98 way; it is very verbose for the caller, but avoids any
allocation for the caller, and can be passed efficiently downstream.
I'm proposing a fourth way, std::function_ref. It acts like
std::function, but only refers to a function; it does not capture the
value. It is the responsibility of the caller to ensure the function is
alive.
void foo(int a, int b, std::function_ref<void (int a, int b)> func);
Here's a sample program:
void sum_and_report(int a, int b, function_ref<void (int sum)> report) {
report(a + b);
}
int main(int ac, char** av) {
int sum;
sum_and_report(2, 2, [&] (int result) {
sum = result;
});
return sum == 4 ? 0 : 1;
}
and here's an example implementation:
template <typename Ret, typename... Args>
class function_ref<Ret (Args...)> {
using thunk_type = Ret (*)(const function_ref*, Args...);
const void* _lambda;
thunk_type _thunk;
public:
template <typename Lambda>
function_ref(const Lambda& lambda)
: _lambda(&lambda) {
_thunk = thunk_type([] (const function_ref* zis, Args... args) {
auto lambda = static_cast<const Lambda*>(zis->_lambda);
return (*lambda)(std::move(args)...);
});
}
Ret operator()(Args... args) const {
return _thunk(this, std::move(args)...);
}
};