Home > Net >  accessing std::tuple element by constexpr in type
accessing std::tuple element by constexpr in type

Time:03-08

I would like to access to a tuple element at compile time by a value constexpr in the type

#include <iostream>
#include <tuple>
#include <utility>

struct A {
    static constexpr int id = 1;
    void work() {
            std::cout << "A" << std::endl;
    }
};

struct B {
    static constexpr int id = 2;
    void work() {
            std::cout << "B" << std::endl;
    }
};

int main() {
    A a;
    B b;
    std::tuple<A,B> t = std::make_tuple(a,b);

    static constexpr int search_id = 2;

    auto& item = std::get< ? ( T::id == search_id ) ? >(t);
    item.work();
    return 0;
}

I guess using std::apply and test would be a runtime search... I'm using c 20

CodePudding user response:

Instead of std::get a single element, you can use std::apply to iterate over the elements of the tuple and perform operations based on the element type

A a;
B b;
auto t = std::make_tuple(a, b);
static constexpr int search_id = 2;

std::apply([](auto&... items) {
  ([]<class T>(T& item) {
    if constexpr (T::id == search_id)
      item.work();
  }(items), ...);
}, t);

Demo

If you really want to get a single tuple element with a specific id value, you can still use std::apply to expand the id of all elements and find the offset of the value equal to search_id as the template parameter of std::get

auto& item = std::apply([&t]<class... Args>(const Args&... items) -> auto& {
  constexpr auto id = [] {
    std::array ids{Args::id...};
    return ids.end() - std::ranges::find(ids, search_id);
  }();
  return std::get<id>(t);
}, t);

item.work();

CodePudding user response:

You can create constrexpr function to get index:

template <typename... Ts>
constexpr std::size_t get_index(int id)
{
    constexpr int ids[] = {Ts::id...};

    const auto it = std::find(std::begin(ids), std::end(ids), id);

    // Handle absent id.
    if (it == std::end(ids)) {
        throw std::runtime("Invalid id");
    }
    // You can also possibly handle duplicate ids.

    return std::distance(std::begin(ids), it);
}

template <int id, typename... Ts>
constexpr auto& get_item(std::tuple<Ts...>& t)
{
    return std::get<get_index<Ts...>(id)>(t);
}
template <int id, typename... Ts>
constexpr const auto& get_item(const std::tuple<Ts...>& t)
{
    return std::get<get_index<Ts...>(id)>(t);
}

and then

auto& item = get_item<search_id>(t);

CodePudding user response:

I'm confused by this line:

static constexpr int search_id = 2;

What is purpose of it?

Note that using a type alias solves problem in clean way and you can still get id value:

int main() {
    A a;
    B b;
    std::tuple<A,B> t = std::make_tuple(a,b);

    using search = B;

    auto& item = std::get<search>(t);
    item.work();

    std::cout << search::id << '\n';
    
    return 0;
}

https://godbolt.org/z/se5jxMWdv

Note also that code is more maintainable, no magic value 2.

  • Related