基于独立的有状态的Reader和Writer基实现ReaderWriter类

Implementing a ReaderWriter class based upon separate stateful Reader and Writer bases

本文关键字:Writer 实现 ReaderWriter Reader 于独立 独立 状态      更新时间:2023-10-16

假设我有两个类…

我们可以称第一个为FooReader,它看起来像这样:

class FooReader {
public:
    FooReader(const Foo* const foo)
    : m_foo(foo) {
    }
    FooData readFooDataAndAdvance() {
        // the point here is that the algorithm is stateful
        // and relies upon the m_offset member
        return m_foo[m_offset++];
    }
private:
    const Foo* const m_foo;
    size_t m_offset = 0; // used in readFooDataAndAdvance
};

我们可以把第二个称为FooWriter,它看起来像这样:

class FooWriter {
public:
    FooWriter(Foo* const foo)
    : m_foo(foo) {
    }
    void writeFooDataAndAdvance(const FooData& foodata) {
        // the point here is that the algorithm is stateful
        // and relies upon the m_offset member
        m_foo[m_offset++] = foodata;
    }
private:
    Foo* const m_foo;
    size_t m_offset = 0;
};

这两种方法都能很好地发挥作用。现在假设我想创建一个FooReaderWriter类。注意

我很自然地想说这个新类是"FooReader"answers"FooWriter";接口只是两个类的合并,语义保持不变。我不想重新实现完美的成员函数。

可以使用继承对这种关系建模,如下所示:

class FooReaderWriter : public FooReader, public FooWriter { };

这很好,因为我得到了共享接口,我得到了实现,我很好地建模了类之间的关系。但是也存在一些问题:

    Foo*成员在基类中是重复的。这是在浪费内存。
  • m_offset成员对于每个基类型是单独的,但它们需要共享它(即调用readFooDataAndAdvancewriteFooDataAndAdvance都应该推进相同的m_offset成员)。

我不能使用PIMPL模式并将m_foom_offset存储在那里,因为我会失去m_foo指针在基础FooReader类中的const-ness。

我还能做些什么来解决这些问题,而不重新实现这些类中包含的功能?

这似乎已经为mixin模式准备好了。最基本的基类只声明成员:

template <class T>
class members {
public:
    members(T* f) : m_foo(f) { }
protected:
    T* const m_foo;
    size_t m_offset = 0;
};

然后我们在它周围写一些包装符来增加阅读:

template <class T>
struct reader : T {
    using T::T;
    Foo readAndAdvance() {
        return this->m_foo[this->m_offset++];
    };
};

和写作:

template <class T>
struct writer : T {
    using T::T;
    void writeAndAdvance(Foo const& f) {
        this->m_foo[this->m_offset++] = f;
    }
};

然后在适当的时候使用

using FooReader = reader<members<Foo const>>;
using FooWriter = writer<members<Foo>>;
using FooReaderWriter = writer<reader<members<Foo>>>;

CRTP.

template<class Storage>
class FooReaderImpl {
public:
  FooData readFooDataAndAdvance() {
    // the point here is that the algorithm is stateful
    // and relies upon the m_offset member
    return get_storage()->m_foo[get_storage()->m_offset++];
  }
private:
  Storage const* get_storage() const { return static_cast<Storage const*>(this); }
  Storage * get_storage() { return static_cast<Storage*>(this); }
};
template<class Storage>
class FooWriterImpl {
public:
  void writeFooDataAndAdvance(const FooData& foodata) {
    // the point here is that the algorithm is stateful
    // and relies upon the m_offset member
    get_storage()->m_foo[get_storage()->m_offset++] = foodata;
  }
private:
  Storage const* get_storage() const { return static_cast<Storage const*>(this); }
  Storage * get_storage() { return static_cast<Storage*>(this); }
};
template<class T>
struct storage_with_offset {
  T* m_foo = nullptr;
  std::size_t m_offset = 0;
};
struct FooReader:
  FooReaderImpl<FooReader>,
  storage_with_offset<const Foo>
{
  FooReader(Foo const* p):
    storage_with_offset<const Foo>{p}
  {}
};
struct FooWriter:
  FooWriterImpl<FooWriter>,
  storage_with_offset<Foo>
{
  FooWriter(Foo* p):
    storage_with_offset<Foo>{p}
  {}
};
struct FooReaderWriter:
  FooWriterImpl<FooReaderWriter>,
  FooReaderImpl<FooReaderWriter>,
  storage_with_offset<Foo>
{
  FooReaderWriter(Foo const* p):
    storage_with_offset<Foo>{p}
  {}
};

如果你需要一个运行时多态的抽象接口,继承FooReaderImplFooWriterImpl

此时,FooReaderWriter服从FooReaderFooWriter的ducktype契约。因此,如果您使用类型擦除而不是继承,则(在使用时)它将满足任何一种要求。

我很想把它们改成
using FooReader = std::function<FooData()>;
using FooWriter = std::function<void(FooData const&)>;

,然后实现对FooReaderWriter的多重签名std::function。但是我很奇怪,而且有点精神错乱。