2016-05-30 3 views
2

Я знаю, что этот вопрос был встречен во многих разных формах снова и снова, и, возможно, это одна из самых запутанных точек в дизайне C++, но я пытаюсь выучить язык через много лет, делая много вещей в чистом C, который считался бы совершенно незаконным в мире C++ (мышление функции указателей жонглирование). Я хочу попытаться воспитывать себя в умении C++, прежде чем сдаваться и перейти на другой язык.C++ virtual templated method

Я только что начал проект, в котором наиболее фундаментальным компонентом является потоковый класс, и для этого я хотел бы, чтобы он был общим: какие данные он будет передавать в соответствии с его подклассами.

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

Моя идея состояла в том, чтобы объекты цепи вместе, как в выходе одного объекта определенного класса будучи непосредственно прочитан другим объектом другого класса. Но для этого все объекты должны иметь возможность принять общую функцию read() и вернуть желаемый тип. Например, у меня есть класс для сращивания бит, который принимает байты (символ без знака) в качестве входов:

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

Он возвращает тип bool и нуждается любой класс, который является производным от BasicStream и имеет типа возвращаемого <unsigned char> в качестве входных данных. Моя идея заключалась в том, чтобы сделать вход полностью агностиком из источника данных; будь то файл, интернет-поток или даже некоторая позиция в памяти; все обернуты вокруг классов, полученных от BasicStream<unsigned char>.

Пример может быть FileReader класса для обработки/синхронной загрузки файла:

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

Теперь, давайте предположим, что я хочу, чтобы создать BitExtractor используя FileReader в качестве источника данных.

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

Внутренне BitExtractor является вызовом метода FileReader read(). Мое предположение заключалось в том, что поскольку FileReader является классом, производным от BasicStream<unsigned char>, он должен распознавать шаблонную функцию.

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 ошибки:

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 

Я уже узнал, хотя другие вопросы StackOverflow, что мой код невозможно в C++, учитывая его отсутствие полиморфизм времени выполнения (компилятор не может решить, какой шаблон BasicStream подкласс запускается во время выполнения). Мой вопрос заключается в том, что, учитывая мою структуру потоковой передачи/цепочки, существует ли какая-либо другая альтернатива «C++ ish» для реализации моего дизайна, например, использование или подклассификация чего-либо из STL (чего я почти ничего не знаю)?

Или это просто неприменимо в C++?

+0

В вашем коде не указан вопрос, о котором вы говорите в своем названии. Базовый класс шаблона может ввести виртуальную функцию (даже такую, которая зависит от параметра типа шаблона класса). Когда вы создаете экземпляр шаблона, в таблице vtable есть только одна четко определенная запись. – StoryTeller

+2

Ваша проблема, вероятно, в коде, который вы не показываете. – StoryTeller

+0

Какая ошибка вы получаете, вероятно, из-за того, что у вас что-то не реализовано. «мой код невозможно в C++, учитывая его отсутствие полиморфизма во время выполнения» !! ?? Кто вам сказал, это ? :) C++ поддерживает как статический, так и временный полиморфизм. – Arunmu

ответ

5

Как стоит проблема в том, что вы объявляете функцию-член шаблона виртуального:

virtual T read(); 

... но вы не определили его; поэтому вы получаете ошибку времени соединения - для vtable для класса BasicStream<bool> нужна функция, на которую указывает точка, и ее нет. Я уверен, что проблема может быть устранена либо путем чистых виртуальных:

virtual T read() = 0; 

... или предоставление определения по умолчанию.

1

Конечно, вы должны использовать потоки из стандартной библиотеки.

Единственная нетривиальная функции вы упоминаете читает логическое значение из потока, который делается с соответствующим operator>> из станда :: basic_istream. Если у вас есть дополнительные особые потребности, вы можете переопределить его в подклассе ad hoc.

Код в вопросе имеет искусственное ограничение того, что поток является однородной последовательностью значений определенного типа с очень неудобным параметром шаблона. В действительности поток следует рассматривать как источник или приемник значений любого типа (вы можете читать или писать что-либо в любой позиции) или последовательность байтов (которые считываются, записываются и преобразуются из и в значения других типов) , Стандартные потоки библиотек выполняются как с перегруженным, так и с шаблоном оператором >> и оператором < < и с характерным низкоуровневым вводом-выводом и позиционированием.

+2

С одной стороны, стандартные потоки библиотек были предназначены для чтения и записи форматированного текста. Код OP явно указывает на желание читать двоичные файлы. OP может обернуть что-то вроде streambuf, но это только виртуализирует исходный конец вещей. Во-вторых, ответы типа «вы изобретаете колесо» не решают вопрос и лучше оставляют комментарии, а не ответы. – Spencer

+0

@Spencer: в то время как форматы вывода отличаются друг от друга, вы можете игнорировать текстовые части '' и адаптировать общий шаблон. В частности, идея «T * buffer» сложна; это означает, что вы не можете смешивать выходные данные из двух разных типов буферов, вы потеряете относительный порядок. '' вместо буферов. – MSalters

+0

@MSalters Но это точно мое намерение; Я не хочу смешивать разные типы данных. Каждый класс должен принимать только один тип и возвращать другой (будь то тот же или другой тип). Я использую шаблоны в основном для обеспечения этого. – MVittiS