Template metaprogram to find similar consecutive typenames
Asked Answered
H

3

5

I am new to template meta programming and was trying to create a program that would find if a parameter pack has consecutive same type names. For example <int, int>, <int, char, char> would return true and <int,char> and <int, char, int> would not.

I managed to write this piece of code but it seems to be comparing each value of parameter pack with itself. I am just looking for a way to iterate through the values of parameter pack to compare with it's consecutive element.

template<typename T, typename U>
struct sameTypename{
    enum {value = false};
};

template<typename T>
struct sameTypename<T, T>{
    enum {value = true};
};

template <typename T, typename ...args>
struct consTypename{
    enum {value = (sameTypename<consTypename<args...>, consTypename<args...>>::value)};
};

template <typename T>
struct consTypename<T, T>{
    enum {value = true};
};

template <typename T>
struct consTypename<T>{
    enum {value = false};
};
Horal answered 5/11, 2018 at 9:30 Comment(2)
What do you think sameTypename<consTypename<args...>, consTypename<args...>>::value is going to do?Detached
You seem to have missed your examples "For example , would return true and and would not."Detached
H
8

Here you go:

#include <type_traits>

template <typename ...P> struct has_adjacent_same_types : std::false_type {};
template <typename A, typename B, typename ...P> struct has_adjacent_same_types<A, B, P...>
    : std::bool_constant<std::is_same_v<A,B> || has_adjacent_same_types<B, P...>::value> {};

I used : std::false_type {}; and : std::bool_constant<X> {}; instead of
{enum{value = false};}; and {enum{value = X};}; respectively, but that's simply a matter of preference.


Some of the features I used are from C++17. If you're using an older version, note that:

std::bool_constant and std::is_same_v are available only starting from C++17 (but that you can use std::integral_constant and std::is_same<>::value before).

(c) @max66

Holpen answered 5/11, 2018 at 9:35 Comment(2)
Nice. But the OP tagged only C++; we don't know if he can use C++17; we can say only that he can use at least C++11 because he use variadic templates. I suggest to signal that std::bool_constant and std::is_same_v are available only starting from C++17 (but that you can use std::integral_constant and std::is_same<>::value before).Peoples
I didn't mention the version of C++ because I wasn't bounded by it. This did the trick and I learned how to deal with parameter packs. I am using enum to get the values and my own definition of sameType instead of is_same_v. Thank you.Horal
P
1

A variation of the HolyBlackCat's solution.

template <typename ...>
struct has_adjacent_same_types : public std::false_type
 { };

template <typename T0, typename ... Ts>
struct has_adjacent_same_types<T0, T0, Ts...> : public std::true_type
 { };

template <typename T0, typename T1, typename ... Ts>
struct has_adjacent_same_types<T0, T1, Ts...>
   : public has_adjacent_same_types<T1, Ts...>
 { };

Two simpler specializations instead of only one, more complex.

Substantially is the same things (I suppose) but I find it a little clear to read and understand.

Peoples answered 5/11, 2018 at 13:6 Comment(0)
P
1

I propose also a completely different solution that uses template folding (so only C++17 or newer, unfortunately) instead of template recursion.

template <typename...>
struct sae_helper;

template <typename ... Ts, typename ... Us>
struct sae_helper<std::tuple<Ts...>, std::tuple<Us...>>
   : public std::bool_constant<(std::is_same_v<Ts, Us> || ...)>
 { };

template <typename ... Ts>
struct some_adjacent_equal
   : public sae_helper<std::tuple<void, Ts...>, std::tuple<Ts..., void>>
 { };

If void is a possible type in the list of type to check, calling sae_helper from some_adjacent_equal instead of void must be used a different type, obviously.

I suppose that this solution is preferable, over a recursive one, when the list of types is very long because avoid compilers template-recursion limits.

If you can use C++14, you can use a constexpr function instead of template folding (and a tag-type instead of void) as follows

template <typename ... Ts, typename ... Us>
constexpr bool sae_helper (std::tuple<Ts...> const &,
                           std::tuple<Us...> const &)
 {
   using unused = bool[];

   bool ret { false };

   (void)unused { true, ret |= std::is_same<Ts, Us>::value... };

   return ret;
 }

struct no_type
 { };

template <typename ... Ts>
struct some_adjacent_equal
   : public std::integral_constant<bool, sae_helper(std::tuple<no_type, Ts...>{},
                                                    std::tuple<Ts..., no_type>{})>
 { };

but, this way, you loose short-circuiting in or evaluation.

Peoples answered 5/11, 2018 at 13:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.