Всем привет!
Пытаюсь понять как реализовать класс для кастомного протокола обмена данными. Я обмениваюсь с девайсом подобными пакетами:
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
.
Если же у вас есть абстрактный базовый класс, то весь интерфейс нужно содержать в нем, чтобы можно было бы через указатель класса вызвать все нужные функции. Так как вы проделали хорошую работу в сторону улучшения:
Я приведу пример реализации, а вы старайтесь понять логику и написать свою версию так, как удобней для решения вашей задачи
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;
}
class DataType: AbstractField {...}
это описание эквивалентно
class DataType: private AbstractField {...}
т.к. права доступа при наследовании в классах по умолчанию - private. Замените на public, и сможете положить по указателю на базовый класс объект класса, наследуемого от AbstractField.
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Я хочу сделать игру, ее уровни будут хранится в формате XML точнее говоря там будет для каждого тайла прописан тег и как ни странно файлы будут...
Итак, создать такую функцию нельзя, знаюОднако мне очень требуется
Попробовал, перепеписать код из книжки Head First про паттерны, на C++, но появляется ошибка E0322 object of abstract class type "MallardDuck" is not allowed: Duck d:\Code\CODE\C++\Duck\Duck\Sourcecpp...