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.
2
Answers
The issue you’re encountering is due to the fact that
std::degrees::lazy_split_view
returns a range ofstd::degrees::subrange items
, notstd::string_view
.You can convert a
std::degrees::subrange
to astd::string_view
by the usage of thebottom()
member function ofstd::degrees::subrange
to get the underlying range, and then building astd::string_view
from that.You can modify your
parse
function to do this:This will construct a
std::string_view
from thestd::ranges::subrange
returned by thelazy_split_view
, which can then be passed to yoursvtox
function. Note that this assumes that the underlying range of thesubrange
is contiguous, which is the case forstd::string_view
. If you were splitting a different type of range, you would need to ensure that it is also contiguous.The subranges split by
lazy_split_view
only modelforward_range
at most, which is why it is called "lazy". Constructingstring_view
requirescontiguous_range
.Instead, you can use
split_view
and explicitly transform the split subranges tostring_view
:Note that compared to
lazy_split_view
,split_view
is not const-iterable, so you cannot declare it as a const object.Demo