【问题标题】:Sparing User from Specifying Policy Template Parameter避免用户指定策略模板参数
【发布时间】:2011-08-02 03:54:52
【问题描述】:

我正在设计一种istream_iterator(称为my_istream_iterator),旨在从输入流中提取单词。处理从迭代器中提取的单词的方式与流中单词的分隔方式无关,但单词本身可能遵循多种格式中的一种。为了适应这一点,我希望用户能够在使用输入流创建my_istream_iterator 时指定策略类,而无需用户在迭代器的模板参数列表中指定策略类的类型。例如,如果我想以行优先顺序输出 CSV 文件中的条目,我希望能够执行以下操作:

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_istream_iterator end;

std::ostream_iterator<char*> out(std::cout, ", ");

// Print the words, delimited by commas, to stdout.
std::copy(begin, end, out);

在创建mystream_iterator 时,即使策略类在迭代器内部使用,我如何才能节省指定策略类类型的用户表单?这可能吗?

感谢您的帮助!


如果有帮助,my_istream_iterator 类的定义可能如下所示:

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;

【问题讨论】:

    标签: c++ templates design-patterns iterator istream-iterator


    【解决方案1】:

    对于您的任务,由于std::istream_iterator&lt;T&gt; 使用Toperator&lt;&lt;(),因此无需构建自己的istream 迭代器。例如:

    #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));
    }
    

    【讨论】:

    • 感谢我完全忽略的简单解决方案!
    【解决方案2】:

    类似于以下内容:

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

    然后确保每个策略都继承自 my_istream_policy_wrapper_base,并为 my_istream_iterator 编写一个适当的构造函数,采用 ifstreammy_istream_policy_base*。请注意,您需要将策略保存在 std::shared_ptr 中或执行其他操作来管理其生命周期。

    效率不是特别高,但相对容易编写和使用。

    【讨论】:

    • 我想过使用虚函数,但是我打算使用流来读取千兆字节的数据,所以我想避免开销;我应该在问题中提到这一点。我想知道这是否仍然可能;我认为必须在某处明确提及该类型,以便编译器能够推断出要调用的正确函数。
    • 从根本上说,如果 my_istream_iterator 不带类型参数,则只有一个 my_istream_iterator::operator++。需要某种间接性才能使其能够调用多个不同的策略对象。
    • 我想没有办法,那么。想把它写成答案吗?我将等待大约一天,以检查是否有其他人提出了回应;如果没有,我会把你的标记为正确的。
    猜你喜欢
    • 1970-01-01
    • 2011-08-18
    • 2014-02-17
    • 1970-01-01
    • 2014-01-24
    • 1970-01-01
    • 1970-01-01
    • 2015-11-01
    • 1970-01-01
    相关资源
    最近更新 更多