Базовые и унаследованные объекты для переноса переменных stdint в C++
Я рефакторинг некоторого кода C++ для проекта AVR, который использует Sloeber (плагин Arduino для Eclipse). В проекте есть много переменных "настроек", которые хранятся в EEPROM, имеют верхний и нижний пределы, требуют строковых меток и т. Д. Эти настройки имеют различные целочисленные типы (uint8_t
, int32_t
и т. д.), и я хотел бы, чтобы оболочка содержала любой из этих типов, а некоторые методы были унаследованы от базового класса. Я также хотел бы иметь возможность сформировать единый массив всех переменных настроек, чтобы я мог их перебирать.
Простая реализация для демонстрации заключается в следующем:
// Base class storing a uint8_t by default
class Base {
public:
typedef uint8_t numType;
numType value = 0;
};
// Child class changing 'numType' to a uint16_t
class Child: public Base {
public:
typedef uint16_t numType;
};
Затем выполните следующее:
Base baseObj;
baseObj.value = 123;
Child childObj;
childObj.value = 12345;
Я хотел, чтобы childObj.value
будет uint16_t
, в то время как baseObj.value
останется uint8_t
,
тем не мение childObj.value
оценивает 57
так что это все еще рассматривается как uint8_t
, Есть какие-нибудь мысли о способе достижения такого рода вещи?
2 ответа
То, что вы хотите, это форма стирания типа. Вы можете использовать std::any
напрямую (это будет иметь смысл, если вы сохраните только значение) или создайте свое собственное:
class Setting {
public:
Setting(std::string description) : _description(description) {}
virtual ~Setting() = 0;
std::string getDescription();
template<typename T>
T getValue();
private:
std::string _description;
};
template <typename T>
class SettingTyped : public Setting {
public:
SettingTyped(T value, std::string description)
: Setting(description), _value(value) {}
T getValue() { return _value; }
private:
T _value;
};
Setting::~Setting() = default;
template<typename T>
T Setting::getValue()
{
auto* typedSetting = dynamic_cast<SettingTyped<T>*>(this);
if (!typedSetting)
throw std::runtime_error("Accessing with wrong type!");
return typedSetting->getValue();
}
template<typename T>
auto makeSetting(T value, std::string description)
{
return std::make_unique<SettingTyped<T>>(value, description);
}
bool foo() {
std::vector<std::unique_ptr<Setting>> settings;
settings.push_back(makeSetting<int>(3, "a setting"));
return (settings[0]->getValue<int>() == 3);
}
Вы можете поиграть с этим, чтобы понять, как провести различие между "настройкой прототипа" (значения по умолчанию, границы) и "текущим значением настройки" (фактическим сохраненным значением). Например, вы все еще можете решить, должны ли границы быть закодированы в типе настроек (и вы создаете отдельный тип для каждого вида настроек) или границы являются (потенциально) разными константами для каждого экземпляра. Требования для этого не ясны из вашего вопроса.
Особенно непонятно, как вы ожидаете знать правильный тип для каждого параметра при их итерации. Здесь предполагается, что вы как-то знаете (например, на основании описания?).
Если у дочерних классов не будет другого уникального поведения, вам нужно только Base
как шаблон:
template<typename T>
struct Base
{
using numType = T;
numType value = 0;
};
Затем вы можете создать псевдонимы этого типа для различных целочисленных типов:
using Uint8 = Base<uint8_t>;
using Uint16 = Base<uint16_t>;