如何编写一个策略友好的输入过滤器?

时间:2014-03-21 14:37:46

标签: templates c++11 visual-studio-2013 crtp template-mixins

背景
我们的软件使用多个API来执行文件i / o: FILE*CStdio(以及多种衍生品),HANDLE,...

我为FilePointer编写了一个FILE* RAII包装器,它很好地替代了所有现有的C代码。

较新的代码通常使用CStdio派生或包装类。

最近,除了我们之前版本的MBCS之外,我还写了SimpleTextFile来处理UTF-16LE i / o。

这些不同类的接口类似,但不完全相同。我以为我可以使用策略模板类编写一些实用算法来使效用算法适应各种文件类型。这有点成功,但是,我经常需要混合某种线读取器 - 过滤器,通常在实用算法中。

这就是摩擦的来源 - 如果我在一个行读取器与过滤器中混合,那么传递给它的任何算法都不能再使用策略类来弄清楚如何适应基础类型(因为R现在是Wrapper<R>,并且Wrapper<R>没有政策。

问题:
我如何制作mixin模板类,它可以为现有类型提供新行为,同时仍然允许在基础类型上工作的各种策略继续工作?

详细信息:
政策模板:
StreamPositionPolicy<T> - 提供适用于T的GetPosition()和SetPosition()。 LineReaderPolicy<T> - 提供一组通用接口,用于从T读取一行。 FileNamePolicy<T> - 为T提供GetFilename()。

因此,如果T是CStdio派生词或FILE *,则上述内容将尽力为搜索,阅读行和检索原始文件名提供通用接口。

另外,我有:
FilteredStringReader<F,R>将过滤器与读者结合在一起。以前,我这样做是:

template <typename Filter, typename Reader>
class FilteredStringReader
{
    Filter      m_filter;
    Reader &    m_reader;

public:

// Constructors
    FilteredStringReader(
        Filter      filter,
        Reader &    reader
    ) :
        m_filter(filter),
        m_reader(reader)
    {
    }

    bool ReadString(CString & strLine)
    {
        return ReadFilteredString(m_reader, m_filter, strLine);
    }
};

这适用于使用LineReaderPolicy&lt;&gt;的任何算法,因为默认策略是尝试使用ReadString()接口,并且此接口与默认(通用)策略匹配,并且生活很好。

但是,如果将此对象传递给需要使用其他策略之一的算法(例如StreamPositionPolicy<FilteredStringReader<F,R>>),则此方案会中断! StreamPositionPolicy<>没有FilteredStringReader<>,而FilteredStringReader<>不适合默认StreamPositionPolicy<>(它只提供行读者界面,而不是流接口或名称接口等。)

所以,我认为这样的mixin可能应该使用CRTP,并从其底层文件类型/ reader类型派生。然后,它将是其中之一,并且任何具有底层读者专业化的策略类都将成功。

但这会带来生命/所有权/复制问题:

template <typename Filter, typename Reader>
class FilteredStringReader : public Reader
{
    Filter      m_filter;

public:

// Constructors
    FilteredStringReader(
        Filter      filter,
        Reader &    reader
    )
        : Reader(reader)
        , m_filter(filter)
    {
    }

    bool ReadString(CString & strLine)
    {
        return ReadFilteredString(m_reader, m_filter, strLine);
    }
};

令人惊讶的是,这种工作 - 构建这个策略对象是可能的......但它复制了读者 - 实例(这可能不是一个好主意,取决于读者的实现 - 或者更可能 - 一些读者类型只是不允许复制)。

我只想要一个我的阅读器对象的实例 - 一个被mixin模板实例包围的实例,仅此而已。

所以,我觉得这是在走错路。

我可以使用可变参数模板,并可能使用完美转发来使我的mixin构建自身+就地基础。但是它失去了前一个版本的一些功能:显示的FilteredStringReader<F,R>的原始版本可以分层放在阅读器之上,使用,然后丢弃,而阅读器本身的生命周期继续(或者更多的包装)深深地为另一个算法的目的)。

因此,使用CRTP似乎不合适。但是,我回到原来的问题,即如何为一个类型R制作包装器,它只拦截一个界面,而让所有其他界面单独使用?

1 个答案:

答案 0 :(得分:1)

您可以尝试为遵循基础读者政策的过滤器提供政策的部分专业化:

template <typename Filter, typename Reader>
class FileNamePolicy<FilteredStringReader<Filter, Reader>>: public FileNamePolicy<Reader> {};

假设策略方法通过引用引用读者,那么您只需要提供转换运算符:

template <typename Filter, typename Reader>
class FilteredStringReader
{
    Filter      m_filter;
    Reader &    m_reader;

public:
    operator Reader &() { return m_reader; }
    operator const Reader &() const { return m_reader; }
    // ...
};