I was working on a simple parser using std::ranges. I was trying to parse ints in a string until all were converted or one failed by doing something like:
try_parse_ints(str) | take_while(is valid int)
But in case of an error, I wanted to obtain the last result corresponding to the error so that I could return it to the user.
So I thought I'd like to have a views::take_while_inclusive that would stop only after returning the first element not respecting the predicate.
I tried to implement this using existing std::views and came up with a dirty trick using views::take_while and a mutable hidden in the predicate like this:
constexpr auto take_while_inclusive(auto&& predicate) {
const auto custom_predicate =
[predicate =
std::forward<decltype(predicate)>(predicate)](auto&& value) {
static bool found = true;
return std::exchange(found, predicate(value));
};
return std::views::take_while(custom_predicate);
}
I know that the take_while predicate should be const but I don't know how else to do it and I have no idea how to implement it using a custom view.
Could you help me implement it properly?
EDIT 1
I forgot to mention it in the first version of this question but I'm looking for a solution acting like a standard std::views by generating the range on the fly and iterate to the input range only once.
EDIT 2
Although @Caleth's answer is excellent in general cases, I also forgot to mention that I'd like to be able to use it at compile time.
struct parsed_ints { range<int> numbers; optional<string> first_error; }
, rather than arange<variant<int, string>>
or whatever – Carveyrange<variant<int, string>>
i'm actually returning a range ofexpected<int, error>
. This is a pretty common things to do in languague like rust where ranges is the default way of doing things. But, my use case aside, this view could be useful in many scenarios in my opinon. – Aneycollect
, which results inExpected<Collection<T>, E>
, i.e. it discards the valid elements if one is an error. – Carvey