Массив шаблонов

256
31 марта 2018, 16:56

Всем привет!

Пытаюсь понять как реализовать класс для кастомного протокола обмена данными. Я обмениваюсь с девайсом подобными пакетами:

AB 01 02 04 B8 07 0D 07 FE 
AB 01 02 04 99 07 1D 07 AF 
AB 01 02 04 78 07 22 07 45 
AB 01 02 | 04       | 19 07     | 15 07 BD A5 | B3
            uint8_t     uint16_t    float

Исходя из адреса девайса и номера команды я могу понять, в каком месте посылки лежат данные того или иного типа (uint8_t, float, int, и т.д.).

Соответственно, я хочу сделать универсальный класс для девайсов. После создания объектов класса настраивать их, задавая типы данных, количество данных, команду и т.д., а дальше просто заполнять данными, оно само конвертируется в массив байт в зависимости от текущего типа данных и отправит посылку на порт. То же самое со чтением данных с порта.

Но мне нужно как-то запомнить в какой последовательности и какие данные лежат. Решил сделать шаблонный класс для разных типов данных и ими заполнять вектор. Т.е. в определенной ячейке вектора лежит объект класса, у которого известен тип данных, соответственно, размер, и можно заодно человеческие данные туда положить. Но вот как-то не хочет это работать.

Сырые прототипы:

class AbstractField{
public:
    virtual ~AbstractField(){}
    virtual int getSize(){}
};
template<class T>
class DataType:public AbstractField{
    T value;
    union {
        T data;
        uint8_t bytes[sizeof(T)];
    } r2b; //result to byte array
public:
    T dataCalc(){
    }
    virtual int getSize(){
        return sizeof(value);
    }
};
//
class Device: public QObject{
    Q_OBJECT
    ComPort *port;
    int command;
    QVector<AbstractField*> w_data;
    QVector<AbstractField*> r_data;
    QVector<DataType<int>> test;
public:
    Device(int command = 0x00);
    ~Device(){}
    void setPort(ComPort *port);
    void setWDataType(AbstractField *dataType, int count);
    void setRDataType(AbstractField *dataType, int count);
    void setData(int i, AbstractField *data);
    AbstractField *getRData(int i);
    void send();
private slots:
    void packageAnalysis(QByteArray data);
};

Сам вопрос: каким путем еще можно решить мою проблему и если моя идея неплохая, то как ее довести до ума?

Edit 1 (Краткое описание проблемы)

Девайс отправляет такой пакет в шестнадцатеричной системе

AB 01 02 04 19 07 15 07 BD A5 B3

Здесь 04 - uint8_t, 19 07 - uint16_t, 15 07 BD A5 - float. Девайсы могут быть разными, соответственно посылки могут быть разной длины и типов данных.

Я хочу сделать единый класс Device, в котором будет храниться вектор w_data (для записи и r_data для чтения) объектов DataType.

Например,

QVector<DataType> w_data;
w_data[0]; //- это DataType<uint8_t>
w_data[1]; //- это DataType<uint16_t>

Я не могу просто так создать вектор объектов шаблонного класса, поэтому пришлось подсовывать ссылку на виртуальный базовый класс AbstractField.

Answer 1
Если же у вас есть абстрактный базовый класс, то весь интерфейс нужно содержать в нем, чтобы можно было бы через указатель класса вызвать все нужные функции. Так как вы проделали хорошую работу в сторону улучшения:

Я приведу пример реализации, а вы старайтесь понять логику и написать свою версию так, как удобней для решения вашей задачи

using namespace std;
// если вы получаете данные в виде строки
using Type = string;
class AbstractField{
public:
    virtual ~AbstractField() = default;
    virtual int getSize() = 0;
    virtual void read(const Type&) = 0;
    virtual void send() const = 0;
    // и другие все функции, которые нужно вызвать через AbstractField*
};    
template<size_t sz>
class DataType:public AbstractField{
    // тут  обьект, во что храним полученные данные.
    //Это может быть строка или вектор, массив: все что вам удобно
    using T = vector<uint16_t>;  // если вы хотите хранить в векторе данные
    T val;
public:
    T dataCalc(){}
    // определяем все виртуальные функции
    // спецификатор virtual не обязательно указывать: компилятор сам поймет
    int getSize(){ return sz; }
    // read реализуем так, чтобы  строго прочитать только sz штук данных
    void read(const Type&) {}
    void send() const {}
    //...    
};
// вы даже можете написать предикат, по которому найдете нужный элемент
// в стандартных альгоритмах (или свооих)
bool predicat(AbstractField* pa, size_t k)
{      return  pa->getSize() == k;  }
int main()
{        
    AbstractField* p = new DataType<1>();
    string s;
    p->read(s);
    p->send();
    // например
     p = new DataType<3>();
    vector<int> v(p->getSize());
    // это вектор с количеством элементов размера обьекта DataType
    // на что указывет  AbstractField* p
    //...
    cout << v.size() <<endl; // 3
    vector<AbstractField*> va;
    va.emplace_back(p);
    if(predicat(va[0], 3))
        cout << "ok";
    return 0;
}
Answer 2
class DataType: AbstractField {...} 

это описание эквивалентно

class DataType: private AbstractField {...}

т.к. права доступа при наследовании в классах по умолчанию - private. Замените на public, и сможете положить по указателю на базовый класс объект класса, наследуемого от AbstractField.

READ ALSO
Парсер XML для создания игр

Парсер XML для создания игр

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

245
Не может считывать из файла символ ; C++

Не может считывать из файла символ ; C++

Например, в файле есть текст ;s

234
C++ виртуальная шаблонная функция(псевдо)

C++ виртуальная шаблонная функция(псевдо)

Итак, создать такую функцию нельзя, знаюОднако мне очень требуется

276
C++ паттерн &ldquo;стратегия&rdquo;

C++ паттерн “стратегия”

Попробовал, перепеписать код из книжки Head First про паттерны, на C++, но появляется ошибка E0322 object of abstract class type "MallardDuck" is not allowed: Duck d:\Code\CODE\C++\Duck\Duck\Sourcecpp...

221