In a compile-time parser, I need to extract parts of a std::string_value
literal and decode them.
A minimal, unfortunately not compiling, example of what I do is:
#include <charconv>
#include <iostream>
#include <optional>
#include <ranges>
#include <string_view>
template <typename X>
static constexpr X svtox(const std::string_view &sv)
{
X value;
auto result = std::from_chars(sv.data(), sv.data() + sv.size(), value);
return value;
}
static constexpr float parse(const std::string_view &sv)
{
const std::ranges::lazy_split_view tokens(sv, " ");
auto token_iterator = tokens.cbegin();
const auto keyword = *token_iterator++;
return svtox<float>(*token_iterator++);
}
int main()
{
const auto sv = std::string_view{"Whatever 1.0"};
float f = parse(sv);
std::cout << f;
}
For sure the real patterns are more complex and also more gets extracted, error handling is included and further processed instead of just printed.
When I call GCC 13.2 with c++ -std=gnu++2b -Wall -o test test.cpp
, I get the following errors:
test.cpp: In function ‘constexpr float parse(const std::string_view&)’:
test.cpp:20:25: error: invalid initialization of reference of type ‘const std::string_view&’ {aka ‘const std::basic_string_view<char>&’} from expression of type ‘std::basic_const_iterator<std::ranges::lazy_split_view<std::basic_string_view<char>, std::ranges::ref_view<const char [2]> >::_OuterIter<true> >::__reference’ {aka ‘std::__common_reference_impl<const std::ranges::lazy_split_view<std::basic_string_view<char>, std::ranges::ref_view<const char [2]> >::_OuterIter<true>::value_type&&, std::ranges::lazy_split_view<std::basic_string_view<char>, std::ranges::ref_view<const char [2]> >::_OuterIter<true>::value_type, 3, void>::type’}
20 | return svtox<float>(*token_iterator++);
| ^~~~~~~~~~~~~~~~~
test.cpp:8:50: note: in passing argument 1 of ‘constexpr X svtox(const std::string_view&) [with X = float; std::string_view = std::basic_string_view<char>]’
8 | static constexpr X svtox(const std::string_view &sv)
| ~~~~~~~~~~~~~~~~~~~~~~~~^~
The environment I am in can be recreated using the following Dockerfile
(simplified example):
FROM mcr.microsoft.com/devcontainers/cpp:1-ubuntu
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && apt-get -y dist-upgrade && apt-get install -y ubuntu-release-upgrader-core && do-release-upgrade -p -f DistUpgradeViewNonInteractive -m server --allow-third-party && apt-get -y dist-upgrade && do-release-upgrade -d -f DistUpgradeViewNonInteractive -m server --allow-third-party && apt-get -y dist-upgrade
I am fairly new to modern (means for me after C++98) C++, so I guess I miss something simple.
The subranges split by lazy_split_view
only model forward_range
at most, which is why it is called "lazy". Constructing string_view
requires contiguous_range
.
Instead, you can use split_view
and explicitly transform the split subranges to string_view
:
auto tokens = sv | std::views::split(' ')
| std::views::transform([](auto r) { return std::string_view(r); });
auto token_iterator = tokens.cbegin();
Note that compared to lazy_split_view
, split_view
is not const-iterable, so you cannot declare it as a const object.