Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get the position of a tuple element

For example, I have a tuple

std::tuple<int, int, int, int> a(2, 3, 1, 4);

and I want to get the position of its elements using such as the the following function.

int GetPosition(const std::tuple<int, int, int, int>& tp, int element);

Here 2's position is 0, 3's position is 1, 1's position is 3 and 4'position is 3. How to implement the function? A silly way is to

int GetPosition(const std::tuple<int, int, int, int>& tp, int element)
{
    if (std::get<0>(tp) == element) return 0;
    if (std::get<1>(tp) == element) return 1;
    if (std::get<2>(tp) == element) return 2;
    ... // Write as more as an allowed max number of elements
}

Any better ways? Thanks.

like image 348
user1899020 Avatar asked Jan 01 '26 08:01

user1899020


1 Answers

UPDATE:

I eventually figured out a way to achieve this in a simpler way that also uses short-circuiting (and therefore performs less comparisons).

Given some machinery:

namespace detail
{
    template<int I, int N, typename T, typename... Args>
    struct find_index
    {
        static int call(std::tuple<Args...> const& t, T&& val)
        {
            return (std::get<I>(t) == val) ? I :
                find_index<I + 1, N, T, Args...>::call(t, std::forward<T>(val));
        }
    };

    template<int N, typename T, typename... Args>
    struct find_index<N, N, T, Args...>
    {
        static int call(std::tuple<Args...> const& t, T&& val)
        {
            return (std::get<N>(t) == val) ? N : -1;
        }
    };
}

The function that clients are going to invoke eventually boils down to this simple trampoline:

template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
    return detail::find_index<sizeof...(Args), T, Args...>::
           call(t, std::forward<T>(val));
}

Finally, this is how you would use it in your program:

#include <iostream>

int main()
{
    std::tuple<int, int, int, int> a(2, 3, 1, 4);
    std::cout << find_index(a, 1) << std::endl; // Prints 2
    std::cout << find_index(a, 2) << std::endl; // Prints 0
    std::cout << find_index(a, 5) << std::endl; // Prints -1 (not found)
}

And here is a live example.


EDIT:

If you want to perform the search backwards, you can replace the above machinery and the trampoline function with the following versions:

#include <tuple>
#include <algorithm>

namespace detail
{
    template<int I, typename T, typename... Args>
    struct find_index
    {
        static int call(std::tuple<Args...> const& t, T&& val)
        {
            return (std::get<I - 1>(t) == val) ? I - 1 :
                find_index<I - 1, T, Args...>::call(t, std::forward<T>(val));
        }
    };

    template<typename T, typename... Args>
    struct find_index<0, T, Args...>
    {
        static int call(std::tuple<Args...> const& t, T&& val)
        {
            return (std::get<0>(t) == val) ? 0 : -1;
        }
    };
}

template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
    return detail::find_index<0, sizeof...(Args) - 1, T, Args...>::
           call(t, std::forward<T>(val));
}

Here is a live example.


ORIGINAL ANSWER:

This does not really sound like a typical way one would use tuples, but if you really want to do this, then here is a way (works with tuples of any size).

First, some machinery (the well-known indices trick):

template <int... Is>
struct index_list { };

namespace detail
{
    template <int MIN, int N, int... Is>
    struct range_builder;

    template <int MIN, int... Is>
    struct range_builder<MIN, MIN, Is...>
    {
        typedef index_list<Is...> type;
    };

    template <int MIN, int N, int... Is>
    struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
    { };
}

template<int MIN, int MAX>
using index_range = typename detail::range_builder<MIN, MAX>::type;

Then, a couple of overloaded function templates:

#include <tuple>
#include <algorithm>

template<typename T, typename... Args, int... Is>
int find_index(std::tuple<Args...> const& t, T&& val, index_list<Is...>)
{
    auto l = {(std::get<Is>(t) == val)...};
    auto i = std::find(begin(l), end(l), true);
    if (i == end(l)) { return -1; }
    else { return i - begin(l); }
}

template<typename T, typename... Args>
int find_index(std::tuple<Args...> const& t, T&& val)
{
    return find_index(t, std::forward<T>(val), 
                      index_range<0, sizeof...(Args)>());
}

And here is how you would use it:

#include <iostream>

int main()
{
    std::tuple<int, int, int, int> a(2, 3, 1, 4);
    std::cout << find_index(a, 1) << std::endl; // Prints 2
    std::cout << find_index(a, 2) << std::endl; // Prints 0
    std::cout << find_index(a, 5) << std::endl; // Prints -1 (not found)
}

And here is a live example.

like image 59
Andy Prowl Avatar answered Jan 03 '26 23:01

Andy Prowl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!