Stack Overflow Asked on December 7, 2021
According to this problem, we can using C++20’sstd::views::split
to split std::string_view
into range of std::string_view
s:
std::string_view s = "this should be split into string_views";
auto views = s | std::views::split(' ')
| std::views::transform([](auto&& rng) {
return std::string_view(&*rng.begin(), std::ranges::distance(rng));
});
If I want to store those std::string_view
s into some tuple like objects such as Boost.Fusion.Sequence
, std::tuple
, or std::array
:
// magic function
auto tuple_like = to_tuple_like(views);
How can I do that? Is there any solution that no need to create intermediary like std::vector
? Note that the origin s
is not constexpr
.
Here is an example implementation of the "magic function" to turn your view into a tuple of string views
#include <iostream>
#include <algorithm>
#include <array>
#include <ranges>
#include <string_view>
#include <tuple>
template <std::size_t tup_size>
struct TupleType {
};
/*
must be manually defined for each size you want to support
*/
template <>
struct TupleType<6> {
using type = std::tuple<
std::string_view,
std::string_view,
std::string_view,
std::string_view,
std::string_view,
std::string_view>;
};
template <>
struct TupleType<7> {
using type = std::tuple<
std::string_view,
std::string_view,
std::string_view,
std::string_view,
std::string_view,
std::string_view,
std::string_view>;
};
template <std::size_t idx, class Tup, class It>
constexpr void TupleAssignImpl(Tup& tup, It& it) {
std::get<idx>(tup) = *it;
++it;
}
template <class Tup, class It>
constexpr void AssignEachImpl(Tup& tup, It& it) {
}
template <std::size_t idx, std::size_t ... Is, class Tup, class It>
constexpr void AssignEachImpl(Tup& tup, It& it) {
TupleAssignImpl<idx>(tup, it);
AssignEachImpl<Is...>(tup, it);
}
template <class Tup, class It, std::size_t ... Is>
constexpr void AssignEach(Tup& tup, It& it, std::index_sequence<Is...>) {
AssignEachImpl<Is...>(tup, it);
}
template <std::size_t size, class Range>
constexpr auto ToTuple(Range const& rng) {
auto tup = typename TupleType<size>::type{};
auto it = std::ranges::begin(rng);
AssignEach(tup, it, std::make_index_sequence<size>{});
return tup;
}
int main() {
constexpr std::string_view s = "this should be split into string_views";
constexpr auto views = s | std::views::split(' ')
| std::views::transform([](auto&& rng) {
return std::string_view(&*rng.begin(), std::ranges::distance(rng));
});
constexpr auto sz = std::distance(
std::ranges::begin(views),
std::ranges::end(views));
auto tup = ToTuple<sz>(views);
static_assert(std::is_same_v<decltype(tup), std::tuple<
std::string_view,
std::string_view,
std::string_view,
std::string_view,
std::string_view,
std::string_view>>);
std::cout << std::get<0>(tup) << std::endl;
std::cout << std::get<1>(tup) << std::endl;
std::cout << std::get<2>(tup) << std::endl;
std::cout << std::get<3>(tup) << std::endl;
std::cout << std::get<4>(tup) << std::endl;
std::cout << std::get<5>(tup) << std::endl;
}
output:
this
should
be
split
into
string_views
a few comments:
In general there may be (probably are) better ways to achieve this. this is just my approach.
I feel it is a bit clunky to have to manually define specializations of TupleType
for each size of tuple you want to support. although since the type of the tuple must strictly be known at compile time I don't know another approach.
With AssignEach
i would have much rather written a simple for loop. e.g.
for (std::size_t i = 0; i < size; ++i) {
std::get<i>(tup) = *it;
++it;
}
but of course the argument to std::get
must strictly be known at compile time so I used AssignEach
as a workaround.
the size of the tuple must be supplied as a template parameter to ToTuple
since, again, in order to work we must guarantee that the tuple size is known at compile time.
as a final note: as others have pointed out, maybe the call to use std::tuple
as opposed to a homogeneous range is questionable here. But you asked how it could be done, and this is an approach.
edit:
if you are okay with using std::array
which is tuple-like, the approach can be much simpler. no need for the specializations of TupleType
and the AssignEach
workaround.
#include <iostream>
#include <algorithm>
#include <array>
#include <ranges>
#include <string_view>
template <std::size_t size, class Range>
constexpr auto ToTuple(Range const& rng) {
auto array = std::array<std::string_view, size>{};
std::copy(std::ranges::begin(rng), std::ranges::end(rng),
array.begin());
return array;
}
int main() {
constexpr std::string_view s = "this should be split into string_views";
constexpr auto views = s | std::views::split(' ')
| std::views::transform([](auto&& rng) {
return std::string_view(&*rng.begin(), std::ranges::distance(rng));
});
constexpr auto sz = std::distance(
std::ranges::begin(views),
std::ranges::end(views));
auto tup = ToTuple<sz>(views);
for (const auto str : tup) {
std::cout << str << std::endl;
}
}
note: output is the same as the tuple example
Answered by UnforeseenPrincess on December 7, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP