How to get the index of an element in a tuple via address?

烂漫一生 提交于 2019-12-07 11:18:09

问题


For example:

std::tuple<int, double> t;
void* p = &std::get<1>(t);

Now I want to get p's index by some function like

template<typename... Ts>
size_t index(void* p, std::tuple<Ts...> const& t)
{
   ...
}

Sure the result is 1 for this example. I am interested in how to implement the function to get the index when p is obtained by ways other than the explicit index.


回答1:


Something you do not need in C++14, a mini indexes library:

template<unsigned...>struct indexes{using type=indexes;};
template<unsigned Cnt,unsigned...Is>
struct make_indexes:make_indexes<Cnt-1,Cnt-1,Is...>{};
template<unsigned...Is>
struct make_indexes<0,Is...>:indexes<Is...>{};
template<unsigned Cnt>
using make_indexes_t=typename make_indexes<Cnt>::type;

Function that does actual work. Creates an array pointers-to-elements. Then searches for p. The nullptr and -1 make it work for empty tuples.

template<unsigned...Is,class Tuple>
unsigned index_of(indexes<Is...>,void const* p, Tuple const&t){
  void const* r[]={ nullptr, &std::get<Is>(t)... };
  auto it = std::find( std::begin(r), std::end(r), p );
  if (it==std::end(r))
    return -1;
  else
    return (it-std::begin(r))-1;
}

you can put that in a details namespace.

The final function:

template<class...Ts>
unsigned index_of( void const*p, std::tuple<Ts...> const& t ){
  return index_of( make_indexes_t<sizeof...(Ts)>{}, p, t );
}

return unsigned(-1) on failure.




回答2:


Logarithmic complexity in terms of comparisons is possible - under the assumption that adresses of consecutive elements are decreasing:

#include <iostream>
#include <algorithm>
#include <tuple>

// minimalistic index-list implementation

template <std::size_t...> struct index_list {using type = index_list;};

template <typename, typename> struct concat;
template <std::size_t... i, std::size_t... j>
struct concat<index_list<i...>, index_list<j...>> : index_list<i..., j...> {};

// (Inefficient) linear recursive definition of make_indices:
template <std::size_t N> struct make_indices :
    concat<typename make_indices<N-1>::type, index_list<N-1>> {};
template <> struct make_indices<0> : index_list<> {};
template <> struct make_indices<1> : index_list<0> {};

// </>

template <typename T, typename = typename make_indices<std::tuple_size<T>::value>::type> struct offsets;

template <typename... T, std::size_t... indices>
struct offsets<std::tuple<T...>, index_list<indices...>>
{
    static std::size_t index_of(void* p, std::tuple<T...> const& t)
    {
        static const std::tuple<T...> tuple;

        static const std::ptrdiff_t array[]
        {
            reinterpret_cast<char const*>(&std::get<indices>(tuple)) - reinterpret_cast<char const*>(&tuple)...
        };

        auto offset_p_t = static_cast<char const*>(p) - reinterpret_cast<char const*>(&t);

        auto iter = std::lower_bound( std::begin(array), std::end(array), offset_p_t, std::greater<>() );

        if (iter == std::end(array)
         || *iter != offset_p_t)
            return -1;

        return iter - std::begin(array);
    }
};


template <typename... T>
std::size_t index_of(void* p, std::tuple<T...> const& t) {return offsets<std::tuple<T...>>::index_of(p, t);}

int main ()
{
    std::tuple<int, double, std::string> t;
    void* p = &std::get<2>(t);

    std::cout << index_of(p, t);
}

Demo.



来源:https://stackoverflow.com/questions/26204253/how-to-get-the-index-of-an-element-in-a-tuple-via-address

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!