Monday, January 13, 2014

Type Functions: loops and lambdas

pfultz2 on reddit suggested type functions may work well with generic lambdas. I thought I'd try it, but my first attempt ran into N3797 5.1.2/2:
...A lambda-expression shall not appear in an unevaluated operand...

I was going to give up on keeping the function calls inside decltype() (one of my replies to his thread), but then I noticed that I placed my lambda directly in the decltype(). It worked once I moved the lambda into a function; this is the result.

Correction to my previous post (thanks to KrzaQ2 on reddit): these examples rely on C++1y; C++11 doesn't have auto return type deduction for functions.

First some boilerplate from my previous post...

#include <assert.h>
#include <iostream>
#include <type_traits>
#include <typeinfo>

using namespace std;

template<class T>
struct Type
{
    using type = T;
};

template<class T>
auto ptr(Type<T>)
{
    return Type<T*>{};
}

template<int i>
using int_ = integral_constant<int, i>;

#define EXTRACT_TYPE(e) decltype(e)::type

#define ASSERT_EXTRACT_TYPE(a, b) \
    assert(typeid(EXTRACT_TYPE(a)) == typeid(b))
loopn composes a functor to itself n times. I test it by passing in a generic lambda.
// Loop 0 times: don't call F
template<class Arg, class F>
auto loopn(int_<0>, Arg arg, F)
{
    return arg;
}

// Loop n times
template<int n, class Arg, class F>
auto loopn(int_<n>, Arg arg, F f)
{
    return loopn(int_<n-1>{}, f(arg), f);
}

// Convenience wrapper
template<int n, class Arg, class F>
auto loopn(Arg arg, F f)
{
    return loopn(int_<n>{}, arg, f);
}

// exampleLoop<n>() produces an int****...
template<int n>
auto exampleLoop()
{
    return loopn<n>(
        Type<int>{},
        [](auto arg){return ptr(arg);});
}

void testLoopn()
{
    // This doesn't work because of N3797 5.1.2/2:
    //      ...A lambda-expression shall not appear 
    //      in an unevaluated operand...
    // ASSERT_EXTRACT_TYPE(
    //     loopn<2>(
    //         Type<int>{},
    //         [](auto arg){return ptr(arg);}),
    //     int**);

    ASSERT_EXTRACT_TYPE(exampleLoop<0>(), int);
    ASSERT_EXTRACT_TYPE(exampleLoop<1>(), int*);
    ASSERT_EXTRACT_TYPE(exampleLoop<2>(), int**);
    ASSERT_EXTRACT_TYPE(exampleLoop<10>(), int**********);

    // Stress test; we're getting close to the default template
    // nesting depth limits in clang and Visual C++ Nov
    // 2013 CTP.
    decltype(exampleLoop<200>())::type p{};
}

int main()
{
    testLoopn();

    cout << "Tests passed" << endl;
}

No comments:

Post a Comment