C++虚拟模板方法

C++ virtual templated method

我知道这个问题已经以许多不同的形式一次又一次地得到回答,并且可能是 C++ 设计中最令人困惑的地方之一,但我在尝试学习这门语言多年后,在纯语言中做了很多事情C 在 C++ 世界中被认为是完全非法的(想想函数指针杂耍)。在放弃并转向另一种语言之前,我想尝试让自己了解 C++ 的思维方式。

我刚刚开始了一个项目,其中最基本的组件是流 class,为此我希望它是通用的:它将流式传输哪种数据取决于它的子classes.

template <typename T>
class BasicStream {
protected:
    T *buffer;
    unsigned int bufferSize;
    unsigned int bufferPos;
    bool streamEnd=false;
public:
    virtual T read();
};

我的想法是将对象链接在一起,就像某个 class 的一个对象的输出被另一个 class 的另一个对象直接读取一样。但要使其工作,所有对象都必须能够接受通用 read() 函数和 return 所需的类型。例如,我有一个 class 来拼接接受字节(无符号字符)作为输入的位:

class BitExtractor : public BasicStream<bool> {
private:
    unsigned char bitMask;
    unsigned char byte;
    BasicStream<unsigned char> &byteSource;

public:
    BitExtractor(BasicStream<unsigned char> &source);
    virtual bool read();
};

它 return 是一个 bool 类型,需要从 BasicStream 派生并具有 <unsigned char> return 类型的任何 class 作为输入.我的想法是让输入与数据源完全无关;无论是文件、互联网流,还是内存中的某个位置;全部包裹在来自 BasicStream<unsigned char>.

的 classes 周围

一个例子是 FileReader class,用于处理 a/synchronous 文件加载:

class FileReader : public BasicStream<unsigned char> {
protected:
    FILE *file;
    bool asyncFlag;
    bool asyncOpReady;
    bool fileEnded;
    pthread_t asyncThread;
    unsigned int lastRead;
public:
    FileReader(char *fileName,int bufferSize=1024,bool asyncRead=false);
    ~FileReader();

    virtual unsigned char read();

private:
    typedef struct {
        unsigned int amount;
        unsigned int *read;
        unsigned char *buffer;
        FILE *file;
        bool *flag;
        bool *flagStreamEnd;
    } TData;
    static void AsyncRead(void *data);
};

现在,假设我想创建一个使用 FileReader 作为数据源的 BitExtractor。

BitExtractor bx=BitExtractor(FileReader("SomeFile.abc"));
bool firstBit = bx.read();

在内部,BitExtractor 正在调用 FileReader read() 方法。我的假设是,由于 FileReader 是从 BasicStream<unsigned char> 派生的 class,它应该识别模板函数。

BitExtractor::BitExtractor(BasicStream<UInt8> &source):bitMask(128),byteSource(source){}

bool BitExtractor::read(){
    bool bit=byte&bitMask;
    if(streamEnd==false){
        bitMask>>=1;
        if(bitMask==0){
            try {
                byte=byteSource.read();
                bitMask=128;
            } catch (...) {
                streamEnd=true;
            }
        }
    }
    else{
        throw "Bytesource has ended!\n";
    }

    return bit;
}

即使编译成功,由于 vtable 错误,它无法 link:

Undefined symbols for architecture x86_64:
  "BasicStream<bool>::read()", referenced from:
      vtable for BasicStream<bool> in BitIO.o
  "BasicStream<unsigned char>::read()", referenced from:
      vtable for BasicStream<unsigned char> in FileIO.o

我已经通过其他 Whosebug 问题了解到我的代码在 C++ 中是不可能的,因为它缺乏运行时多态性(编译器无法决定 subclass 在 BasicStream 的哪个模板调用运行)。我的问题是,考虑到我的数据 streaming/chaining 模式,是否还有其他 "C++ish" 替代方案来实现我的设计,例如使用或子 class 来自 STL 的东西(我几乎知道什么都没有)?

或者它在 C++ 中根本无法实现?

当然你应该使用标准库中的流。

您提到的唯一重要功能是从流中读取布尔值,这是通过 std::basic_istream 的适当 operator>> 完成的。如果您有额外的特殊需求,您可以在临时流子类中覆盖它。

问题中的代码具有人为限制,即假装流是某种类型的值的同构序列,具有非常不方便的模板参数。 实际上,流应该被视为任何类型的值的源或汇(您可以在任何位置读取或写入任何内容)或字节序列(读取、写入以及从其他类型的值转换为其他类型的值)。标准库流通过重载和模板化的 operator>> 和 operator<< 以及基于字符的低级 I/O 和位置记帐。

目前的问题是你将模板成员函数声明为virtual:

virtual T read();

...但是你没有定义它;这就是为什么你会得到一个 link 时间错误 - BasicStream<bool> class 的 vtable 需要一个函数来指向,但没有一个。我很确定可以通过将其设为纯虚拟来解决该问题:

virtual T read() = 0;

...或提供默认定义。