Dec 17, 2014

[C++14] lambda expression type extract tricks

From Paul Fultz II's article:
Static Lambdas: Initializing lambdas at compile time

Interesting tricks that he had applied.

Instead of initializing lambda object at program start-up.

Idea:
1.
lambda expression object is generated during run-time;
however, the type itself for sure is completed during compile time.

2.
Extract the lambda expression type thus could be used for constexpr variable. 


Steps:

1.
Extract ptr to lambda expression type.
Of course, use template function to deduce :-)
this function is not constexpr due to it's parameter isn't literal type.
--
template<typename T>
typename std::remove_reference<T>::type* GetPtrToLambda(T&& t)
{
    return &t;
}

2.
A constexpr template function could extract the type: Wrapper<T> explained later.
--
template<typename T>
constexpr Wrapper<T> GetLambdaWrapper(T*)
{
    return {};
}

3.
GetPtrToLambda([](auto i){cout << i << endl;})
will be run on runtime; however, we only need the type at compile time. Trick Paul Fultz did:
--
true ? nullptr : GetPtrToLambda([](auto i){cout << i << endl;});
--
i.e, will get a ptr to lambda expression type but point to null.

4.
i.e:
--
>GetLambdaWrapper(true ? nullptr : GetPtrToLambda([](auto i){cout << i << endl;}));
--
Now we have the Wrapper<T> while T is the lambda expression type.

5.
Wrapper is straight forward: A stateless Wrapper of size 1 byte. T is the type of lambda expression type.
This works only for stateless lambda expression type.
--
template<typename T>
struct Wrapper
{
    template<typename... A>
    decltype(auto) operator()(A&&... a) const
    {
        return reinterpret_cast<const T&>(*this)(std::forward<A>(a)...);
    }
};

6.
The binary size of the code is roughly the same as simply do:
auto lambda = [](auto i){cout << i << endl;};

7.
Full code:
----
#include <iostream>
#include <type_traits>

using namespace std;

template<typename T>
typename std::remove_reference<T>::type* GetPtrToLambda(T&& t)
{
    return &t;
}

template<typename T>
struct Wrapper
{
    template<typename... A>
    decltype(auto) operator()(A&&... a) const
    {
        return reinterpret_cast<const T&>(*this)(std::forward<A>(a)...);
    }
};

template<typename T>
constexpr Wrapper<T> GetLambdaWrapper(T*)
{
    return {};
}

constexpr auto lambda = GetLambdaWrapper(true ? nullptr : GetPtrToLambda(
    [](auto i){cout << i << endl;}));

int main()
{
    lambda(42);
}

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.