Sparing User from Specifying Policy Template Parameter
I'm designing a kind of istream_iterator
(call it my_istream_iterator
) designed to extract words from an input stream. The manner in which the words extracted from the iterator will be dealt with is independent of the way the words are delimited in the stream, but the words themselves may follow one of many formats. To accommodate for this, I want the user to be able to specify a policy class when creating the my_istream_iterator
using an input stream, without the user having to specify the type of the policy class in the template argument list of the iterator. For example, if I want to output the entries in a CSV file in row-major order, I would like to be able to do something like this:
std::ifstream is("words.csv");
// Assume that the_policy_class is used to read a special kind
// of CSV file that deviates from the standard specification.
// I don't want to have to specify the type of the policy class
// used by the iterator; how would I be able to do this? (The
// value_type of `mystream_iterator` is always char*).
my_istream_iterator begin = csv_begin<the_policy_class>(
is, the_policy_class('\t', '\n', 1));
// Default constructor for end-of-stream iterator.
my_istrea开发者_StackOverflowm_iterator end;
std::ostream_iterator<char*> out(std::cout, ", ");
// Print the words, delimited by commas, to stdout.
std::copy(begin, end, out);
How can I spare the user form specifying the type of the policy class when creating the mystream_iterator
, even though the policy class is internally used by the iterator? Is this possible?
Thanks for your help!
If it helps, the definition of the my_istream_iterator
class will likely look something like this:
template <typename Character, typename CharTraits = std::char_traits<Character>,
typename Distance = std::ptrdiff_t>
class basic_my_istream_iterator : public std::iterator<std::input_iterator_tag,
const Character*, Distance>
{
/* ... */
};
typedef basic_my_istream_iterator<char> my_istream_iterator;
typedef basic_my_istream_iterator<wchar_t> my_wistream_iterator;
For your task there is no need to build your own istream iterator since std::istream_iterator<T>
uses T
's operator<<()
. For example:
#include <iostream>
#include <iterator>
#include <vector>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
template<char field_delim, char row_delim>
struct csv_row
{
std::vector<std::string> fields;
friend std::istream& operator>>(std::istream& s, csv_row& row)
{
std::string line;
getline(s, line, row_delim);
char field_delim2[2] = { field_delim };
boost::split(row.fields, line, boost::is_any_of(field_delim2));
return s;
}
friend std::ostream& operator<<(std::ostream& s, csv_row const& row)
{
// hard-code tab-separated output just for the sake of exposition
std::copy(row.fields.begin(), row.fields.end(), std::ostream_iterator<std::string>(s, "\t"));
return s << '\n';
}
};
int main()
{
typedef csv_row<'|', '\n'> row;
std::vector<row> rows;
std::copy(std::istream_iterator<row>(std::cin), std::istream_iterator<row>(), std::back_inserter(rows));
std::copy(rows.begin(), rows.end(), std::ostream_iterator<row>(std::cout));
}
Something like the following:
class my_istream_policy_base {
virtual ~my_istream_policy_base() = 0;
virtual char* find_next_break(char*, size_t) = 0;
}
template<typename T>
my_istream_iterator csv_begin(std::ifstream is, T pol) {
return my_istream_iterator(is, new T(pol));
}
Then just make sure every policy inherits from my_istream_policy_wrapper_base
and write an appropriate constructor for my_istream_iterator
taking an ifstream
and a my_istream_policy_base*
. Note that you'll need to keep the policy in an std::shared_ptr or do something else to manage its lifetime.
Not especially efficient, but relatively easy to write and use.
精彩评论