2

I have multiple containers with the same element type T. I would like to select one of the containers depending on a enum.

I tried something like this:

auto range = [category,system]() -> auto { switch(category) { case sound_category::voice: return std::ranges::views::all (system->dialogue_voices); // could be std::array case sound_category::mono: return std::ranges::views::all (system->mono_voices); // could be std::vector case sound_category::music: return std::ranges::views::all (system->music_voices); // could be std::list default: return std::ranges::views::all (system->sfx_voices); // could be std::deque } }(); 

but that will result in a compiler error, since the deduced type in the cases is different.

Is there some way to achieve that?

11
  • 1
    @KamilCuk The value type of the containers (sfx_voices, mono_voices etc) is the same and I am only interested in the values/references to these values Commented Oct 7, 2022 at 14:45
  • 4
    Return a std::span of the array you want? Commented Oct 7, 2022 at 14:48
  • 2
    std::span is the way to go. Changing the return type of the lambda to std::span<T> and return std::span(system->...) should work for the purpose. Commented Oct 7, 2022 at 14:49
  • 1
    @NathanOliver while a span could potentially solve my issue now. It won't work if I want to switch out the container in a later development stage (to a non-contiguous one). This is not a question about "performance" or "array vs vector vs list vs deque". My goal is to have a generic way to get a view from a collection selected at runtime. Commented Oct 7, 2022 at 15:03
  • 1
    @Raildex I have as well, feel free to roll back, but I figured removing all mention that these are arrays currently isn't needed since you want a generic solution. Commented Oct 7, 2022 at 15:09

3 Answers 3

2

ranges-v3 has any_view for type erasure view.

So it would be something like:

auto range = [category,system]() -> ranges::v3::any_view<Voice> { switch(category) { case sound_category::voice: return std::ranges::views::all (system->dialogue_voices); // could be std::array case sound_category::mono: return std::ranges::views::all (system->mono_voices); // could be std::vector case sound_category::music: return std::ranges::views::all (system->music_voices); // could be std::list default: return std::ranges::views::all (system->sfx_voices); // could be std::deque } }(); 
Sign up to request clarification or add additional context in comments.

Comments

1

Use a subrange from two iterators.

#include <ranges> #include <array> int main() { std::array<int, 2> a; std::array<int, 3> b; auto c = std::ranges::subrange<int*>(a.begin(), a.end()); c = std::ranges::subrange<int*>(b.begin(), b.end()); } 

3 Comments

That does not compile. godbolt.org/z/8fTbTc9dW
Compiles on gcc, no idea about msvc. Doing explicit template type std::ranges::subrange<int*> works fine. Could be std::range<>::iterator is more special on msvc, dunno
Doesn't compile on clang and msvc.
1

Instead of something like:

auto range = [category,system]() -> auto { switch(category) { case sound_category::voice: // ... } }(); // stuff with range 

You can use a visitor pattern:

auto visitor = [&](const auto& range) { // stuff with range }; switch (category) { case sound_category::voice: return visitor(system->dialogue_voices); case sound_category::mono: return visitor(system->mono_voices); // ... } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.