3

Here is my code:

template<
    template <typename TSEvent,
              typename ...TSEvents> typename V,
    typename... Filtered>
constexpr auto filter() {
    if constexpr(sizeof...(TSEvents) == 0) {
        return type_list<Filtered...>{};
    }
    if constexpr(is_default_constructible<TSEvent>::value) {
        return filter<<TSEvents...>, Filtered...>();
    }
    return filter<<TSEvents...>, Filtered...>();
}

I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?

max66
  • 65,235
  • 10
  • 71
  • 111
Dr.Knowitall
  • 10,080
  • 23
  • 82
  • 133
  • Does it have to be a function? In this case, traits are easier to do, because of partial specializations. – Maikel Jun 26 '18 at 20:14
  • This is a software exercise that I was given by my work. I would use a trait but they framed the solution as a constexpr. I'm just trying to get my metaprogramming skills up. – Dr.Knowitall Jun 26 '18 at 20:16
  • The framed the solution as: constexpr type_list<>filter(){return {};} – Dr.Knowitall Jun 26 '18 at 20:19
  • What is it you're trying to do? This question doesn't really make sense to ask. You're basically asking "How many types are there in `std::tuple`?" Not like... `std::tuple`, but `std::tuple` generally. – Barry Jun 26 '18 at 20:57
  • 1
    Also `filter<, Filtered...>` is not valid syntax. – Barry Jun 26 '18 at 20:57

3 Answers3

2

Usually through another level of indirection, and usually a struct that we can specialize.

For example:

namespace detail
{
    template<class...>
    struct filter_t;

    template<template<class, class...> class V, class TSEvent, class... TSEvents, class... Filtered>
    struct filter_t<V<TSEvent,TSEvents...>, Filtered...>
    {
        static constexpr auto filter() {
            return sizeof...(TSEvents);
         }
    };
} // detail

template<class... T>
constexpr auto filter()
{
    return detail::filter_t<T...>::filter();
}

template<class T, class...U>
struct type_list{};

int main()
{
    std::cout << filter<type_list<int, int, int>, int>();
}

Live Demo

AndyG
  • 39,700
  • 8
  • 109
  • 143
2

Just to present another option, you could do this with only functions.

#include <iostream>
using namespace std;
template<typename...>
struct type_list{};

template < template <typename...> typename T,typename A,typename... B, typename... Filtered> 
constexpr auto filter_impl(T<A,B...>*,type_list<Filtered...>)
{

    using filtered_list = std::conditional_t<is_arithmetic<A>::value,
                        type_list<Filtered...,A>,
                        type_list<Filtered...>>;

    if constexpr (sizeof...(B) == 0)
        return filtered_list();
    else
        return filter_impl( (T<B...>*)0, filtered_list());
}

template <typename T> 
constexpr auto filter()
{
    return filter_impl( (T*)0,type_list<>());
}

struct not_arethmetic{};



int main() {
    auto b = filter< type_list<not_arethmetic,int,bool,not_arethmetic,double> >();
    static_assert(std::is_same< decltype(b) , type_list<int,bool,double>>::value);
    return 0;
}

Demo

One thing, In your original example your first if expression will mean that the final TSEvent is not checked, as it returns if the varadic TSEvents... is zero size, but there will be one final element to check whether is_default_constructible.

Also, you might find this post useful regarding template template parameter names.

rmawatson
  • 1,909
  • 12
  • 20
1

I however get this error, size...(TSEvents), TSEvents is not declared. Is there anyway for me to access TSEvents in my nested template?

Short answer: no.

Long answer: with

template<
    template <typename TSEvent,
              typename ...TSEvents> typename V,
    typename... Filtered>
constexpr auto filter()

you set two template arguments for the filter() function.

The first one, related to the TSEvents variadic list, is a template-template argument that receive one or more types argument.

But your function doesn't receive a type that is based over that template-template (with a fixed TSEvent type and a fixed TSEvents); receive the template-template.

So doesn't make sense the test size...(TSEvents) because, for filter() isn't fixed the TSEvents list.

To explain this in another way... you can call filter this way

filter<std::tuple, short, int, long>();

Ask for sizeof...(TSEvents) is asking how many types contains std::tuple where std::tuple is only the container of types but without contained types.

If you want to make some sort of actions in your filter() function, you need a type template parameter, not a template-template parameter.

It's simpler with classes (see AndyG's answer) where you can use partial specialization (with functions you can't) or with function when they receive arguments from which you can deduce types.

Suppose your filter() receive an object of type V<SomeTypes...> and an object of type std::tuple<Filtered...>, you can write something as follows (caution: code not tested)

template<
    template <typename ...> typename V,
    typename TSEvent, typename ... TSEvents, typename... Filtered>
constexpr auto filter (V<TSEvent, TSEvents...> const & v,
                       std::tuple<Filtered...> const & t) {
   /* some code where you can use also TSEvent and TSEvents... */
}

This way TSEvent and TSEvents... are deduced from the v argument.

max66
  • 65,235
  • 10
  • 71
  • 111
  • Thank you so much for your answer. I think I'm constraining myself to a solution that I think they want to see and not what would make life easier. I'm going to implement the struct. – Dr.Knowitall Jun 26 '18 at 20:57
  • @Dr.Knowitall - if you have only types (and not objects) the AndyG's solution is the only practicable that I see. – max66 Jun 26 '18 at 21:00