2

I am new in c++ metaprogramming and few days ago I decided to write a template with variadic parameter pack, that knows in which position in parameter pack stands first int. More precisely I want to have a struct with name GetIntPos and with static constexpr int value which indicates the position of int in parameter pack starting with 1, and 0 if there is no int parameter type in parameter pack. For example

cout<<GetIntPos<long, char, int, long>::value; // must print 3
cout<<GetIntPos<int, int, long>::value; // must print 1

and

cout<<GetIntPos<long, long, char>::value; // must print 0;

How can it be done?

max66
  • 65,235
  • 10
  • 71
  • 111
Vahag Chakhoyan
  • 873
  • 1
  • 10
  • 21

5 Answers5

4

This is actually pretty simple to solve with a template type-trait:

#include <type_traits> // std::integral_constant
#include <cstddef>     // std::size_t

// Recursive base template (specialized below)
template <std::size_t Current, typename T, typename...Types>
struct find_type_aux;

// Specialization: If we find a match
template <std::size_t Current, typename T, typename...Types>
struct find_type_aux<Current,T,T,Types...>
  : std::integral_constant<std::size_t,Current>
{};

// Specialization: If we haven't found a match, but still have some left in the pack
template <std::size_t Current, typename T, typename Type0, typename...Types>
struct find_type_aux<Current,T,Type0, Types...>
  : find_type_aux<Current + 1, Types...> // Strip off first, and search rest. Increment count
{};

// Specialization: If we didn't find a match
template <std::size_t Current, typename T>
struct find_type_aux<Current,T>
  : std::integral_constant<std::size_t,static_cast<std::size_t>(-1)>{};
{};

// The interface: Find's "T" and returns the 0-based index.
template <typename T, typename...Types>
struct find_type : find_type_aux<0u,T,Types...>{};

With a trait like this, the found element will be the 0-based index, and non-found will be static_cast<std::size_t>(-1). 1

Since you mentioned being 1-indexed with 0 for not found, using find_type<int, Types...>::value + 1 will yield either 0 if not found (due to overflow), or a 1-indexed result -- as requested.


1 The reason this is not explicitly defined to be 1-indexed is that the current definition can be reused to find the index of any type in a variadic pack -- and most interfaces that operate with types that contain variadics expect 0-indexing (such as std::get). This can easily be used as the building blocks specifically for int, such as with a variable template:

template <typename...Types>
constexpr auto find_int_v = find_type<int,Types...>::value + 1;

Which then yields the correct answers from:

int main() {
    std::cout << find_int_v<short, long, char> << '\n';
    std::cout << find_int_v<int, short, long, char> << '\n';
    std::cout << find_int_v<short, long, char, int> << '\n';
    return 0;
}

as

0
1
4
Human-Compiler
  • 11,022
  • 1
  • 32
  • 59
4

This seems to work in my tests:

namespace detail {
    template <int INDEX>
    constexpr int GetIntPosImpl() {
        return 0;
    }

    template <int INDEX, typename T, typename ...Ts>
    constexpr int GetIntPosImpl() {
        if constexpr(std::is_same_v<T, int>) {
            return INDEX;
        }
        else {
            return GetIntPosImpl<INDEX + 1, Ts...>();
        }
    };
}

template <typename ...Ts>
struct GetIntPos {
    static constexpr int value = detail::GetIntPosImpl<1, Ts...>();
};

int main() {
    std::cout << GetIntPos<short, long, char>::value << '\n';
    std::cout << GetIntPos<int, short, long, char>::value << '\n';
    std::cout << GetIntPos<short, long, char, int>::value << '\n';
    return 0;
}

My output:

0
1
4
Stephen Newell
  • 7,330
  • 1
  • 24
  • 28
3

My not-recursive way (just for fun... or for a code obfuscation context)

#include <iostream>
#include <algorithm>
#include <type_traits>

template <int = 0>
auto GIP_helper (std::index_sequence<>)
   -> std::integral_constant<std::size_t, 0u>;

template <typename ... Ts, std::size_t ... Is>
auto GIP_helper (std::index_sequence<Is...>)
   -> std::integral_constant<std::size_t,
       (1u+std::min({(std::is_same<Ts, int>::value
                      ? Is
                      : sizeof...(Is))...})) % (1u+sizeof...(Is))>;

template <typename ... Ts>
using GetIntPos
   = decltype( GIP_helper<Ts...>(std::index_sequence_for<Ts...>{}) );

int main()
 {
   std::cout << GetIntPos<long, char, int, long>::value << std::endl; 
   std::cout << GetIntPos<int, int, long>::value << std::endl;
   std::cout << GetIntPos<long, long, char>::value << std::endl;
 }

Works starting from C++14.

max66
  • 65,235
  • 10
  • 71
  • 111
2

My recursive way

#include <iostream>
#include <type_traits>

template <std::size_t, typename ...>
struct GIP_helper
   : public std::integral_constant<std::size_t, 0u>
 { };

template <std::size_t N, typename T0, typename ... Ts>
struct GIP_helper<N, T0, Ts...>
   : public GIP_helper<N+1u, Ts...>
 { };

template <std::size_t N, typename ... Ts>
struct GIP_helper<N, int, Ts...>
   : public std::integral_constant<std::size_t, N>
 { };


template <typename... Ts>
struct GetIntPos : public GIP_helper<1u, Ts...>
 { };

int main()
 {
   std::cout << GetIntPos<long, char, int, long>::value << std::endl; 
   std::cout << GetIntPos<int, int, long>::value << std::endl;
   std::cout << GetIntPos<long, long, char>::value << std::endl;
 }

Works starting from C++11.

max66
  • 65,235
  • 10
  • 71
  • 111
  • Thank you for this answer. I think this is the most straightforward way. – Vahag Chakhoyan Sep 25 '20 at 19:33
  • @VahagChakhoyan - Uhmmm... a little suggestion: when possible, avoid recursion. Take also in count that there are limits (compiler specifics, but configurable) regarding the number of recursion steps. My other answer it's more cryptic (maybe improvable) but I think is better. Well... maybe non my exact solution... but the idea. – max66 Sep 25 '20 at 20:46
0

Here is a solution using Boost.MP11:

#include <boost/mp11/algorithm.hpp>

template<class... TArgs>
constexpr auto get_int_pos() {
    using list_t = boost::mp11::mp_list<TArgs...>;
    using index_t = boost::mp11::mp_find<list_t, int>;
    return index_t{} == boost::mp11::mp_size<list_t>{} ? 0 : 1 + index_t{};    
}

int main() {
    static_assert(0 == get_int_pos<double>());
    static_assert(0 == get_int_pos<long, long, char>());
    static_assert(1 == get_int_pos<int, int, long>());
    static_assert(3 == get_int_pos<long, char, int, long>());
    static_assert(4 == get_int_pos<short, long, char, int>());
}
Joe
  • 171
  • 1
  • 3