dimanche 24 avril 2016

C++ variadic template partial specialization for non-type arguments

I have a map_n template that applies an N-arity function to each set of elements from N input tuples to produce a new output tuple. All input tuples must be of same length (which I should probably check with static assert).

The code works correctly, except I have not been able to write the recursion termination condition partial specialization in a generic way as shown in the following code snippet.

#include <tuple>
#include <cassert>

namespace impl {
    // car, cdr, cons implementation 
    // 
    template<unsigned... XS>
    struct sequence {
        template<unsigned X>
        using cons = sequence<X, XS...>;
    };

    template<unsigned start, unsigned end>
    struct range {
        static_assert(start < end, "Range: start > end");
        using type = typename range<start + 1, end>::type::template cons<start>;
    };

    template<unsigned start>
    struct range<start, start> {
        using type = sequence<>;
    };

    template<typename T, unsigned... N>
    auto select(const T& t, sequence<N...>) {
        return std::make_tuple(std::get<N>(t)...);
    }

} // end namespace impl

// car, cdr, cons 
// 
// empty list 
// 
constexpr const std::tuple<> empty;

// car 
// 
template<typename T>
auto car(const T& t) { return std::get<0>(t); }

// cdr 
// 
template<typename T, typename R = typename impl::range<1, std::tuple_size<T>::value>::type>
auto cdr(const T& t) {
    return impl::select(t, R());
}

// cons 
// 
template<typename X, typename... XS>
auto cons(X x, const std::tuple<XS...>& t) {
    return std::tuple_cat(std::make_tuple(x), t);
}

namespace impl {
    // map_n implementation 
    template<typename F, typename... Ts>
    struct map_n_impl {
        static auto map(const F& f, const Ts&... t) {
            return cons(
                f(car(t)...), 
                map_n_impl<F, decltype(cdr(t))...>::map(f, cdr(t)...)
                );
            }
        };

    // NOTE: Need a more general specialization here 
    // 
    template<typename F>
    struct map_n_impl<F, std::tuple<>, std::tuple<>> {
        static std::tuple<> map(const F&, const std::tuple<>&, const std::tuple<>&)
        {
            return std::make_tuple();
        }
    }; 
} // end namespace impl

// map_n 
// 
template<typename F, typename... Ts>
auto map_n(const F& f, const Ts&... t) {
    return impl::map_n_impl<F, Ts...>::map(f, t...);
}


int main(int, const char **) {
    {
        auto tup1 = std::make_tuple(1.0, 2.0, 3.0);
        auto tup2 = std::make_tuple(0.0, 1.0, 2.0);
        auto r = map_n([](auto x, auto y) { return x - y; }, tup1, tup2);
        assert(std::get<0>(r) == 1.0);
        assert(std::get<1>(r) == 1.0);
        assert(std::get<2>(r) == 1.0);
    }

    // {
    //  auto tup1 = std::make_tuple(1.0, 2.0, 3.0);
    //  auto tup2 = std::make_tuple(0.0, 1.0, 2.0);
    //  auto tup3 = std::make_tuple(4.0, 5.0, 6.0);
    //  auto r = map_n([](auto x, auto y, auto z) { return x - y + z; }, tup1, tup2, tupe3);
    //  assert(std::get<0>(r) == 5.0);
    //  assert(std::get<1>(r) == 6.0);
    //  assert(std::get<2>(r) == 7.0);
    // }

    return 0;
}

Aucun commentaire:

Enregistrer un commentaire