c++templatesc++20stdtupletype-deduction

How to inject std::basic_istream through constructor without explicitly specifying <CharT,Traits>


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.


Solution

  • 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;
    }
    

    Demo