I want to extract a maximum of N 1 strings from a std::stringstream
.
Currently, I have the following code (that needs to be fixed):
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#include <iterator>
#include <ranges>
#include <algorithm>
int main( )
{
const std::string_view sv { " @a hgs -- " };
const size_t expectedTokenCount { 4 };
std::stringstream ss;
ss << sv;
std::vector< std::string > foundTokens;
foundTokens.reserve( expectedTokenCount 1 );
std::ranges::for_each( std::ranges::take_view { ss, expectedTokenCount 1 }, [ &foundTokens ]( const std::string& token )
{
std::back_inserter( foundTokens );
} );
if ( foundTokens.size( ) == expectedTokenCount )
{
// do something
}
for ( const auto& elem : foundTokens )
{
std::cout << std::quoted( elem ) << '\n';
}
}
How should I fix it? Also, how should I use back_inserter
to push_back
the extracted strings into foundTokens
?
CodePudding user response:
Note that the following aliases are in effect:
namespace views = std::views;
namespace rng = std::ranges;
There are a few issues and oddities here. First of all:
std::ranges::take_view { ss, expectedTokenCount 1 }
It's conventional to use the std::views
API:
ss | views::take(expectedTokenCount 1)
The more glaring issue here is that ss
is not a view or range. You need to create a proper view of it:
auto tokens = views::istream<std::string>(ss) | views::take(expectedTokenCount 1);
Now for the other issue:
std::back_inserter( foundTokens );
This is a no-op. It creates a back-inserter for the container, which is an iterator whose iteration causes push_back
to be called, but doesn't then use it.
While the situation is poor in C 20 for creating a vector from a range or view, here's one way to do it:
rng::copy(tokens, std::back_inserter(foundTokens));
Putting this all together, you can see a live example, but note that it might not be 100% correct—it currently compiles with GCC, but not with Clang.
As noted below, you can also make use of views::split
to split the source string directly if there's a consistent delimiter between the tokens:
std::string_view delim = " ";
auto tokens = views::split(sv, delim);
However, you might run into trouble if your standard library hasn't implemented this defect report.