Базовые и унаследованные объекты для переноса переменных stdint в C ++ - PullRequest
0 голосов
/ 01 февраля 2019

Я рефакторинг некоторого кода 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 ]

0 голосов
/ 01 февраля 2019

То, что вы хотите, это форма стирания типа.Вы можете использовать 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);
}

Демо

Вы можете игратьс этим, чтобы выяснить, как провести различие между «настройкой прототипа» (значения по умолчанию, границы) и «текущим значением настройки» (фактическим сохраненным значением).Например, вы все еще можете решить, должны ли границы быть закодированы в типе настроек (и вы создаете отдельный тип для каждого вида настроек) или границы являются (потенциально) разными константами для каждого экземпляра.Требования для этого не ясны из вашего вопроса.

Особенно непонятно, как вы ожидаете знать правильный тип для каждого параметра при их итерации.Здесь предполагается, что вы как-то знаете (например, на основании описания?).

0 голосов
/ 01 февраля 2019

Если дочерние классы не будут иметь другого уникального поведения, вам понадобится только Base как шаблон :

template<typename T>
struct Base
{
    using numType = T;
    numType value = 0;
};

Затем вы можете создать псевдонимы этого типа дляразличные целочисленные типы, которые вы хотите:

using Uint8 = Base<uint8_t>;
using Uint16 = Base<uint16_t>;
...