Обзор
Короче говоря. Я реализовал самописный фреймворк для Stm32F7 и Stm32F4 для изучения C ++. В своем первом проекте я сильно полагался на виртуальные функции, но в недавней задаче у меня возникли проблемы с производительностью при использовании этого дизайна (задача, относящаяся к микросекундной синхронизации).
Поэтому я попытался использовать полиморфизм stati c, но я не уверен, что это хорошее решение. Я объясню свои проблемы в предоставленных примерах.
Обзор:
У меня есть интерфейс для функций GPIO. В этом сокращенном примере просто write
Тогда есть различные спецификации MCU c реализации этого интерфейса. Я только что представил пример F7, но другие должны быть возможны. И, наконец, у меня есть домен, указанный c код. В этом примере некоторый датчик, который выбирает, использует GPIO для извлечения чего-то.
Внимание! Датчик не должен полагаться на указанное c оборудование. Следует полагаться на предоставленную абстракцию GPIO.
Решения, которые я нашел, кроме виртуальных функций
1: CRTP
// gpio interface
namespace V1 {
template<class D>
class Gpio
{
public:
void write(bool status) {
static_cast<D*>(this)->impl_write(status);
};
};
}
// f7 specific gpio
namespace V1 {
class F7 : public V1::Gpio<F7> {
public:
void impl_write(bool status) {
}
};
}
// sensor
namespace V1 {
template <class GPIO_T>
class Sensor {
private:
Gpio<GPIO_T> *gpio;
public:
Sensor(Gpio<GPIO_T> *gpio) : gpio(gpio) {}
void read() {
return gpio->write(true);
}
};
}
// main
using gpio_f7 = V1::F7;
V1::Gpio<gpio_f7> gpio {};
V1::Sensor<gpio_f7> sensor {&gpio};
Этот код фактически работает, а также устраняет накладные расходы на вызов из виртуальных функций во время компиляции , Но у него есть некоторые недостатки.
Поскольку я полагаюсь на параметры шаблона, все должно быть только заголовком.
Я не могу сохранить абстракцию Gpio *gpio
, поскольку основание (V1::Gpio
) нуждается в производном как шаблон, поэтому должно быть Gpio<GPIO_T> *gpio
. Проблема в том, что это приводит к КАЖДОМУ КЛАССУ, который использует мои абстракции для храмования (как GPIO_T
выше). Что для меня ощущается как огромный дефицит. Но так как я не в индустрии C ++, может, это вполне нормально?
2: Функции шаблонов
Другой вариант, который я нашел, это C -стиль шаблонных функций api.
// interface
namespace V2 {
template<class T>
void write(T& gpio, bool status) {
gpio->write(status);
}
}
// gpio
namespace V2 {
class F7 {
public:
void write(bool status) {
}
};
}
// sensor
namespace V2 {
class Sensor {
private:
F7 *gpio;
public:
Sensor(F7 *gpio) : gpio(gpio) {
}
void read() {
return write(gpio, true);
}
};
}
// main
V2::F7 gpio {};
V2::Sensor sensor {&gpio};
IMO API для C -Style является для меня большим недостатком. Поскольку это скрывает функции, становится легче увидеть, какие функции доступны для объекта с помощью IDE. (Может быть, функции скрыты за разными включаемыми файлами)
Более того, я больше не могу сохранять абстракцию датчика и вынужден напрямую комментировать классы F7. Поэтому мне придется менять КАЖДЫЙ класс, если я хочу использовать другой MCU.
Так что я на самом деле не очень доволен обоими решениями. Я стараюсь переписать свои занятия в стиле CRTP. По крайней мере, для наиболее важных наиболее используемых классов, таких как GPIO, я предпочел бы иметь наибольшую производительность, которую я могу архивировать.
Итак, мои вопросы:
Я неправильно использую CRTP? Есть ли способ избавиться от храмования каждого класса, который использует абстракции? Или это нормально (идиоматизм c?), Чтобы храмовать каждый класс? Существуют ли другие решения (версия C ++ не имеет значения, может быть C ++ 20)?