Using C++11 lambdas to replace Phoenix in MPL expressions

I use Phoenix to essentially turn code into types which can then be manipulated through MPL. I mostly use this to conditionally select algorithms or to include/exclude code at compile time, based on some logic written with MPL. I was interested in writing the code fragments using the new C++11 lambda expressions as it gives a more natural way to write code. While you can get the type of a lambda expression with decltype, the problem I've run into is instantiating the resultant type in order to actually get the function to execute. Lambdas have no default constructor, which makes sense, as there would be no way to deal with the capture variables. So I'm wondering if anybody can shed some light on manually instantiating lambdas. I would think there is a way (possibly shady) to do this, at least for lambdas with no capture. Then passing around lambda types as template parameters would be useful, which I think would lead to many interesting results. I only get the digest, so it I would appreciate it if you CC me on any response. Thanks- Augustus

Augustus Saunders wrote:
Lambdas have no default constructor, which makes sense, as there would be no way to deal with the capture variables. So I'm wondering if anybody can shed some light on manually instantiating lambdas. I would think there is a way (possibly shady) to do this, at least for lambdas with no capture.
For lambdas with no capture, the following code might work: #include <iostream> int main (int argc, char* argv[]) { auto x = [](int i){ std::cout << i << std::endl; }; (*static_cast<decltype(x)*>(0))(5); return 0; } But, I strongly discourage its use. Instead, I recommend wrapping lambdas into boost::optional and constructing with in-place factories: #include <boost/optional.hpp> int main (int argc, char* argv[]) { auto x = [](int i){ return i * 100; }; // Store type and value in x boost::optional<decltype(x)> y; // Get type of x and default construct y y = boost::in_place(x); // Get value of x and lazily construct lambda return 0; } Regards, Michel

----- Original Message -----
auto x = [](int i){ std::cout << i << std::endl; }; (*static_cast<decltype(x)*>(0))(5);
Right, since without a capture, the implicit this pointer passed to the operator() of the anonymous class is never dereferenced and so just static_casting 0 or null won't actually cause an error.
Instead, I recommend wrapping lambdas into boost::optional and constructing with in-place factories:
auto x = [](int i){ return i * 100; }; // Store type and value in x
boost::optional<decltype(x)> y; // Get type of x and default construct y y = boost::in_place(x); // Get value of x and lazily construct lambda
I'm not sure I follow here. Let me make a more concrete example and maybe you can fill in the blank: template <typename LAMBDA> int execute_me(int input) { return (*static_cast<LAMBDA*>(0))(input); // ok now same thing using optional? boost::optional<LAMBDA> y; return y(input); // ?? obviously not but what? } main() { execute_me<decltype([](int i) {return i * 100;})>(5); } Thanks for the ideas! Augustus

Augustus Saunders wrote:
auto x = [](int i){ std::cout << i << std::endl; }; (*static_cast<decltype(x)*>(0))(5);
Right, since without a capture, the implicit this pointer passed to the operator() of the anonymous class is never dereferenced and so just static_casting 0 or null won't actually cause an error.
Unless "Core issue 232" is not resolved, http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232 I think this results in undefined behavior.
Instead, I recommend wrapping lambdas into boost::optional and constructing with in-place factories:
auto x = [](int i){ return i * 100; }; // Store type and value in x
boost::optional<decltype(x)> y; // Get type of x and default construct y y = boost::in_place(x); // Get value of x and lazily construct lambda
I'm not sure I follow here. Let me make a more concrete example and maybe you can fill in the blank:
template <typename LAMBDA> int execute_me(int input) { return (*static_cast<LAMBDA*>(0))(input);
// ok now same thing using optional? boost::optional<LAMBDA> y; return y(input); // ?? obviously not but what? }
The approach with boost::optional cannot do this. In-place factories need a lambda object itself. So maybe you're not interested in this approach. (But, please note that we cannot know the type of lambda expression without actually constructing lambda objects.) Regards, Michel

----- Original Message -----
The approach with boost::optional cannot do this. In-place factories need a lambda object itself. So maybe you're not interested in this approach. (But, please note that we cannot know the type of lambda expression without actually constructing lambda objects.)
Since lambdas are copy-constructable, I'm unclear what problem the boost::optional suggestion was trying to solve. And yes, I have found that indeed you must say: auto x = lambda; decltype(x) rather than just decltype(lambda). I understand that once again captures probably forced this restriction as it interacts with the surrounding scope, but it seems unfortunate that stateless lambdas can't be declared in-place like that. Anyway, it seems like Phoenix and boost::lambda are still the best way to accomplish these kinds of tasks. I was hoping to make C++11 lambdas work as over time Phoenix and boost::lambda will just become more obscure as everybody gets used to the new syntax. Thanks- Augustus

Augustus Saunders wrote:
The approach with boost::optional cannot do this. In-place factories need a lambda object itself. So maybe you're not interested in this approach. (But, please note that we cannot know the type of lambda expression without actually constructing lambda objects.)
Since lambdas are copy-constructable, I'm unclear what problem the boost::optional suggestion was trying to solve.
OK, the boost::optional approach is not useless for your problems. Let's forget about it ;) For stateless lambdas, use the following: // Helper metafunctions to determine the function type of C++11 lambdas template <typename T> struct function_type; template <typename Ret, typename Class, typename... Args> struct function_type<Ret (Class::*)(Args...)> { typedef Ret type(Args...); }; template <typename Ret, typename Class, typename... Args> struct function_type<Ret (Class::*)(Args...) const> { typedef Ret type(Args...); }; template <typename Lambda> struct lambda_function_type { typedef typename function_type<decltype(&Lambda::operator())>::type type; }; // Wrapper class of C++11 lambdas template <typename Lambda> struct lambda { static typename lambda_function_type<Lambda>::type * f_ptr; template <typename... Args> auto operator()(Args&&... args) const -> decltype(f_ptr(static_cast<Args&&>(args)...)) { return f_ptr(static_cast<Args&&>(args)...); } }; template <typename Lambda> typename lambda_function_type<Lambda>::type * lambda<Lambda>::f_ptr = 0; template <typename Lambda> lambda<Lambda> make_lambda(Lambda const& l) { lambda<Lambda>::f_ptr = l; return lambda<Lambda>(); } // Sample code #include <iostream> int main (int argc, char* argv[]) { auto x = make_lambda([](int x, int y){ return x + y; }); decltype(x) y; // Default construct y std::cout << y(5, 9) << std::endl; // Evaluate stateless lambda return 0; } Regards, Michel

On 3/24/2012 7:25 PM, Augustus Saunders wrote:
While you can get the type of a lambda expression with decltype
This is not true. Lambdas are not allowed to appear in non-evaluated contexts, and decltype is one of them. If your compiler allows it, consider it a bug. Don't rely on that behavior. -- Eric Niebler BoostPro Computing http://www.boostpro.com
participants (3)
-
Augustus Saunders
-
Eric Niebler
-
Michel Morin