Шаблон проектирования для аппаратных слотов, для которых требуется создание экземпляра другого класса/объекта и который определяется при загрузке.

Это вопрос на С++ для встроенной пользовательской платы, для которой я разрабатываю программу. Мы будем использовать freeRTOS, если это поможет. Плата имеет 3 физических слота/разъема. 1 слот/разъем имеет 20 контактов и принимает 1 из 3 конкретных плат, но в будущем может быть больше плат. Два других слота/разъема имеют 18 контактов и могут вмещать 1 из 8 плат, но в будущем их может быть больше.

Платы, как правило, обмениваются данными через SPI, но иногда их нужно немного взломать с помощью ручных вызовов GPIO (зависит от того, какая плата установлена). Схема связи не может быть стандартизирована (не то, что я могу придумать) из-за проблем с обратной совместимостью. Платы, которые вставляются в эти слоты, должны иметь возможность подключаться к другим, другим основным платам, если того выберет конечный потребитель.

У меня есть класс SPI, который вызывает базовый аппаратный SPI. У меня есть класс слота, который принимает указатель на объект SPI и включает в себя несколько контактов сброса и несколько тестовых функций, чтобы определить, какая плата подключена к какому слоту. Я создаю 3 таких объекта слота, по одному для каждого физического слота/заголовка.

После определения того, что содержит каждый слот, я пришел к тому, что мне нужно спроектировать/создать/кодировать классы для каждой из плат, но я столкнулся с архитектурной проблемой, по крайней мере, мне так кажется. Поскольку это встроенная система, я хотел бы избежать использования ключевого слова «новый», но это не высечено на камне.

Я думаю, что было бы расточительно создавать копии каждой платы, а затем использовать только те 3, которые были обнаружены.

В моей голове я думаю, что мой класс слота, возможно, должен иметь какой-то указатель void, который можно установить так, чтобы он указывал на объект, экземпляр которого создается после того, как мы определим, что это за доска. В этом случае я почти уверен, что мне придется использовать «новый».

Я знаю, что в C есть концепция «объединения», когда вы создаете какой-то пользовательский тип, который может быть любым из нескольких типов. Когда вы создаете копии этого типа, выделяется достаточно памяти для самого большого члена объединения. Можете ли вы сделать это на C++ и поместить классы в качестве членов объединения, а затем каким-то образом сделать его типом класса, который вы определили для слота?

Есть ли другие дизайнерские идеи?

Любая другая информация, которую необходимо добавить, чтобы кто-то лучше понял проблему?

Спасибо,


person NDEthos    schedule 03.01.2020    source источник
comment
Вы можете использовать обычные объединения C с членами класса в C++ с несколько ограничений. Кроме того, в стандартной библиотеке C++ существует типобезопасный класс объединения, который называется std::variant. (представлен в C++17, но доступен как библиотека с одним заголовком вплоть до C++11 и по крайней мере использовался кто-то на arduino).   -  person parktomatomi    schedule 04.01.2020
comment
Поскольку это встроенная система, я хотел бы избежать использования ключевого слова new, но это не высечено на камне. - могу я спросить, почему? Динамическое выделение памяти очень полезно во встроенной системе, если все сделано правильно, поскольку оно позволяет вам разместить гораздо больше данных в ограниченной оперативной памяти, которая у вас есть, если эти данные не должны существовать одновременно. В таких местах, как те, которые ограничены по времени, вам, конечно, может потребоваться выполнить некоторые оптимизации, которые могут включать избавление от динамического распределения в критическом пути, но глобальный запрет new, вероятно, заходит слишком далеко.   -  person Jacek Ślimok    schedule 04.01.2020


Ответы (1)


Поскольку это встроенная система, я хотел бы избежать использования ключевого слова «новый», но это не высечено на камне.

Вы можете использовать «размещение нового» и статически выделенный буфер памяти, размер которого равен максимальному размеру используемых классов. Примерно так (см. в онлайн-иде):

#include <iostream>

class Base {
public:
    Base() { std::cout << __FUNCTION__ << std::endl; }
    virtual ~Base() { std::cout << __FUNCTION__ << std::endl; }
    virtual void print(void) = 0; 
};

class A : public Base {
public:
    A(int i_) : i(i_) { std::cout << "\t" << __FUNCTION__ << " " << i << std::endl; }
    virtual ~A() { std::cout << "\t" << __FUNCTION__ << " " << i << std::endl; }
    virtual void print(void) { std::cout << "\t\t" << __FUNCTION__ << " " << i << std::endl; }

private:
   int i;
};

class B : public Base {
public:
    B(int i_, int j_) : i(i_), j(j_) { std::cout << "\t" << __FUNCTION__ << " " << i << " " << j << std::endl; }
    virtual ~B() { std::cout << "\t" << __FUNCTION__ << " " << i << " " << j << std::endl; }
    virtual void print(void) { std::cout << "\t\t" << __FUNCTION__ << " " << i << " " << j << std::endl; }

private:
   int i;
   int j;
};

#define SIZE_IN(class, type)  ((sizeof(class)+sizeof(type)-1)/sizeof(type))
#define POOL_PLACE(class) uint8_t place_##class [ sizeof(class) ]

// Just for simple MAX() calculation
union pool_member_sizes
{
    POOL_PLACE(A);
    POOL_PLACE(B);
    // class C, D, E, ...
};

// statically allocate pool memory for set of classes, one pool for each set
// choose required alignment by pool "type"
uint32_t pool[ SIZE_IN(pool_member_sizes, uint32_t) ];

Base *driver;

int main()
{
    driver = new(pool) A(1);
    driver->print();
    driver->~Base();

    driver = new(pool) B(2,3);
    driver->print();
    driver->~Base();

    driver = new(pool) A(4);
    driver->print();
    driver->~Base();
}
person ReAl    schedule 05.01.2020