3

I am trying to check the unspecialised uniqueness of a tuple of specialised types using Boost.Mp11:

#include <iostream>
#include <vector>
#include <deque>
#include <tuple>

#include <boost/mp11/algorithm.hpp>

namespace
{
template <typename T, template <typename...> typename U>
struct is_specialisation : std::false_type {};

template <template <typename...> typename U, typename... Args>
struct is_specialisation<U<Args...>, U> : std::true_type {};

template <template <typename...> typename U>
struct is_specialisation_meta
{
    template <typename T>
    using type = is_specialisation<T, U>;
};

template <typename TypeList>
struct unique_specialisation
{
    template <typename T>
    using type = std::is_same<
        boost::mp11::mp_count_if<
            TypeList,
            is_specialisation_meta<T>::template type // Error!
        >,
        boost::mp11::mp_size_t<1>
    >;
};
}

int main()
{
    using types = std::tuple<
        std::vector<int>,
        std::deque<int>,
        std::tuple<int>
    >;

    using all_unique_specialisations = boost::mp11::mp_all_of<
        types,
        unique_specialisation<types>::template type
    >;

    std::cout << std::boolalpha << all_unique_specialisations::value << std::endl;

    return EXIT_SUCCESS;
}

You can run the above code on Coliru. For each type, the whole list is iterated over trying to find an unspecialised equivalent, so {std::vector<int>, std::deque<float>, std::tuple<Foo>} would pass but {std::vector<int>, std::vector<float>, std::tuple<Foo>} would not.

However I get this error:

main.cpp:30:37: error: type/value mismatch at argument 1 in template parameter list for 'template<template<class ...> class U> struct {anonymous}::is_specialisation_meta'
             is_specialisation_meta<T>::template type
                                     ^
main.cpp:30:37: note:   expected a class template, got 'T'

But I don't understand how T is unknown - can anyone see what I am doing wrong?

cmannett85
  • 21,725
  • 8
  • 76
  • 119
  • 2
    what exactly is an "unspecialised equivalent"? I don't understand your example `{std::vector, std::deque, std::tuple}` vs `{std::vector, std::vector, std::tuple}` – m.s. Nov 27 '18 at 08:19
  • @m.s. `std::vector` is the specialisation of `int` on `std::vector`, therefore `std::vector` is the _unspecialised_ type. The algorithm tries to find multiples of unspecialised types (i.e. it checks for unspecialised uniqueness). The first example passes because each unspecialised type is different, whilst the second fails because there are two `std::vector`s. – cmannett85 Nov 27 '18 at 09:29

2 Answers2

3

The immediate error is in...

template <typename T>
using type = std::is_same<
    boost::mp11::mp_count_if<
        TypeList,
        is_specialisation_meta<T>::template type // Error!
    >,
    boost::mp11::mp_size_t<1>
>;

If we compare it against...

template <template <typename...> typename U>
struct is_specialisation_meta

... we see that the code passes a typename T where a template U is expected. But amending that only shifts the error elsewhere, because now boost.mp is not going to get the predicate type it expects. I'm afraid I'm not too familiar with the library to tell you how to get a working version beyond this.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
1

Running on Wandbox

#include <iostream>
#include <vector>
#include <deque>
#include <tuple>

#include <boost/mp11/algorithm.hpp>

namespace
{
template <typename T, template <typename...> typename U>
struct is_specialisation : std::false_type {};

template <template <typename...> typename U, typename... Args>
struct is_specialisation<U<Args...>, U> : std::true_type {};

template <typename T> struct is_specialisation_meta;//ADDED

template <template <typename... > typename U, typename... Args>//CHANGED
struct is_specialisation_meta<U<Args...>>
{
    template <typename T>
    using type = is_specialisation<T, U>;
};

template <typename TypeList>
struct unique_specialisation
{
    template <typename T>
    using type = std::is_same<
        boost::mp11::mp_count_if<
            TypeList,
            is_specialisation_meta<T>::template type // Error!
        >,
        boost::mp11::mp_size_t<1>
    >;
};
}

int main()
{
    using types = std::tuple<
        std::vector<int>,
        std::deque<int>,
        std::tuple<int>
    >;

    using types2 = std::tuple<
        std::vector<int>,
        std::vector<float>,
        std::tuple<int>
    >;

    using all_unique_specialisations = boost::mp11::mp_all_of<
        types,
        unique_specialisation<types>::template type
    >;

    using all_unique_specialisations2 = boost::mp11::mp_all_of<
        types2,
        unique_specialisation<types2>::template type
    >;

    std::cout << std::boolalpha << all_unique_specialisations::value << std::endl;
    std::cout << std::boolalpha << all_unique_specialisations2::value << std::endl;

    return EXIT_SUCCESS;
}
llonesmiz
  • 155
  • 2
  • 11
  • 20
  • Beautiful, thank you! Can you explain why my approach didn't work? – cmannett85 Nov 27 '18 at 09:40
  • StoryTeller's answer explains that, and you should probably accept it. You were passing a type (`std::vector`) where a "class template" was expected (`std::vector`). I have changed `is_specialisation_meta` to accept a type and deduce from it the corresponding "unspecialised class template". If you pass a type that is not a specialization you'll get another error and you would need to change the behaviour of the primary template. – llonesmiz Nov 27 '18 at 10:00
  • @llonesmiz Please, accept StoryTeller's as the actual answer as suggested – mloskot Feb 01 '19 at 00:25