Topic: Implicit conversion from function pointer to std::nested_function


Author: int19h@gmail.com
Date: Tue, 25 Sep 2007 13:24:51 CST
Raw View
The most recent (N2413) lambda proposal provides some rationale on why
a common representation for all <&>-form lambdas, in form of
std::nested_function type, is a good idea. I agree wholeheartedly - if
this part is accepted, std::nested_function could conceivably replace
std::function where a definite binary implementation is required (i.e.
dynamic libraries), since it can do everything that std::function can
- wrapping any callable object in a lambda is trivial - and is more
likely to be optimized by a compiler; in particular, a simple lambda
forwarding to a function could, depending on the ABI, be represented
directly by a poiner to that function, and thus the callback would be
just as efficient as a usual call via a function pointer.

On the other hand, using std::nested_function to expose callbacks in
the API requires the client of that API to write lambdas even for the
trivial cases, such as the forementioned very common case of using a
plain function as a target of the callback. For example:

extern void register_callback(std::nested_function<bool(int, int)>
callback);

bool callback(int, int) { ... }
.
register_callback(<&>(x,y) (callback(x,y))); // must wrap before
passing

This case, in my opinion, is sufficiently common that there is a
benefit in providing a shorter syntax for it, especially since it can
be done very easily: just define an implicit conversion from any
function pointer to std::nested_function; i.e.:

template<class MoveConstructible R, class MoveConstructible...
ArgTypes>
struct nested_function<R(ArgTypes...)> {
  nested_function(R (*func)(ArgTypes...))
    : this(<&>(ArgTypes... args) -> R (func(args))
  { }
};

Since no variables are captured when creating such a closure, this
conversion is always safe, and can, therefore, be made implicit. This
allows to write, given the same definitions as in the earlier code
snippet:

register_callback(callback); // no need to wrap

One might ask the question: can't this be solved by overloading the
register_callback function, like this:

extern void register_callback(bool (*callback)(int, int));
extern void register_callback(std::nested_function<bool(int, int)>
callback);


And indeed it can, but consider the likely implementation of
register_callback in that case, given the fact that it will likely
need to store the value of its parameter:

std::nested_function<bool(int, int)> registered_callback;

void register_callback(bool (*callback)(int, int)) {
  registered_callback = <&>(x,y)(callback(x,y));
}

void register_callback(std::nested_function<bool(int, int)> callback)
{
  registered_callback = callback;
}

In other words, it will amount to precisely the same thing in terms of
efficiency, but requires the programmer to write additional
boilerplate code for every function definition (and what if a function
takes two callback arguments? three?..).

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]