I'm writing templated CSVParser class, that reads data from any basic_istream object and converts it to std::tuple of given types:
template<class Ch, class Tr, class... Types>
class CSVParser {
public:
explicit CSVParser(std::basic_istream<Ch, Tr> &istream) : istream_(istream) {
}
CSVParser& operator>>(std::tuple<Types...>& tup) {
[&] <size_t... Is> (std::index_sequence<Is...>)
{
((istream_ >> std::get<Is>(tup)), ...);
} (std::index_sequence_for<Types...>());
return *this;
}
private:
std::basic_istream<Ch, Tr>& istream_;
};
I want it to be possible to use this way:
const static std::string sample_csv = "1,abc\n"
"2,def\n"
"3,ghi";
std::stringstream ss(sample_csv);
CSVParser<int, std::string> parser(ss);
std::tuple<int, std::string> data;
parser >> data;
However, Ch and Tr types can't be deduced and I need to explicitly specify them:
CSVParser<char, std::char_traits<char>, int, std::string> parser(ss);
Is it possible in C++20 to have this pair of types deduced, and variadic class... Types
not?
Or how can I rewrite my code to achive expecting result?
I've tried to move variadic parameter pack to the beginning of template arguments list, but compiler argues that it should be at the end.
There is no need to use Types...
as the template parameter of CSVParser
as the former does not contain the tuple
member. A more appropriate option would be to make the CSVParser
have only two template arguments
template<class Ch, class Tr>
class CSVParser {
public:
explicit CSVParser(std::basic_istream<Ch, Tr> &istream) : istream_(istream) {}
// ...
};
This allows you to construct a CSVParser
from a basic_istream
object without explicitly specifying the type
const static std::string sample_csv = /* */;
std::stringstream ss(sample_csv);
CSVParser parser(ss);
Additionally, CSVParser::operator>>
can be simplified using std::apply
CSVParser& operator>>(auto& tup) {
std::apply([this](auto&... elems) {
(istream_ >> ... >> elems);
}, tup);
return *this;
}