在不调用 Arduino 上的构造函数的情况下为堆中的对象保留内存

Reserving memory for an object in heap without calling its constructor on Arduino

我正在做一个 arduino 项目,这是相关的,因为 arduino 本身不支持 STL 或动态分配。我注意到我写的很多 classes 在构建时什么都不做,但是有一个 .init() 方法可以实际初始化任何资源。这是因为这样 class 可以在全局范围内初始化,然后当 setup 函数运行时,实际初始化发生在 .init() 被调用时。

例如:

const portn_t en=A5, rs=A4, d4=A0, d5=A1, d6=A2, d7=A3;
// making room for that object in the global scope
// this way it can be both used in ``setup()`` and ``loop()``
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup() {
  lcd.begin(16, 2);  // Doing the actual initialization
}

void loop() {
  lcd.clear();
  lcd.write("Hello world!");
  delay(500);
}

这适用于使用 initbegin 方法设计的 classes。这种设计模式在大多数 Arduino 库中很常见,但对于未实现这一点的 classes,我目前正在使用它作为解决方法:

Button& get_btn_up() {
  // The Button class actually does initialization at construcion
  static Button bt(2, true);
  return bt;
}

Button& get_btn_enter() {
  static Button bt(3, true);
  return bt;
}

Button& get_btn_down() {
  static Button bt(4, true);
  return bt;
}

void setup() {
  // Initializes all the ``Button`` objects
  get_btn_up();
  get_btn_enter();
  get_btn_down();
}

void loop() {
  auto up = get_btn_up();
  if (up.just_pressed()) {
    ...
  }
  ...
}

我认为这不是最佳解决方案,因为有很多样板文件只是为了实现 new 和一些独特的指针可以完成的事情。 因此,我尝试制作一个 DelayedInit 容器 class,它将在联合中保存对象所需的内存并处理其生命周期

template<typename T>
union MemoryOf {
  uint8_t memory [sizeof(T)];
  T obj;
};

template<typename T>
struct DelayedInit {
private:
  MemoryOf<T> memory{.memory={ 0 }};
  bool is_set = false;
public:
  T& get() const {
    memory.obj;
  }
  DelayedInit() {}
  ~DelayedInit() {
    if (is_set)
      get().~T();
  }
  T* operator->() const {
    return &get();
  }
  T& operator*() {
    is_set = true;
    return get();
  }
  const T& operator*() const {
    return get();
  }
  explicit operator bool() const {
    return is_set;
  }
};

这个实现当时被破坏了,因为每当我尝试调用盒装 class 的任何方法时它都会锁定 arduino。下面是它应该如何使用

DelayedInit<Button> up, enter, down;

void setup() {
  *up = Button(2, true);
  *enter= Button(3, true);
  *down = Button(4, true);
}

void loop() {
  if (up->just_pressed()) {  // Locks up the arduino
    ...
  }
  ...
}

我猜代码中存在一些我不知道的内存管理错误。 DelayedInit 实现中存在哪些错误?有没有更好的方法来解决这个问题?

这是我最终得到的解决方案。 .init 模拟 arduino 库中大多数 类 使用的模式,实际上没有将 init 逻辑拆分为两种方法。肯定有改进的余地,但这适用于全局变量。

#pragma once
#include <new>
template<typename T>
union MemoryOf {
  uint8_t memory [sizeof(T)];
  T obj;
  MemoryOf() : memory{ 0 } {};
  ~MemoryOf() {};
};

// Only tested with global variables
// so there might be bugs in the cleanup bits
template<typename T>
struct DelayedInit {
private:
  MemoryOf<T> memory;
  bool is_set = false;
public:
  const T& get() const {
    return memory.obj;
  }
  T& get() {
    return memory.obj;
  }
  void remove() {
    get().~T();
  }
  DelayedInit() {};
  ~DelayedInit() {
    if (is_set)
      remove();
  }
  template<typename ...Args>
  inline void init(Args ...args) {
    if (is_set) {
      remove();
    }
    new (&memory.memory) T(args...); // == &memory.obj
    is_set = true;
  }
  T* operator->() {
    return &get();
  }
  const T* operator->() const {
    return &get();
  }
  T& operator*() {
    return get();
  }
  const T& operator*() const {
    return get();
  }
  explicit operator bool() const {
    return is_set;
  }
};

用法:

#include "delayed_init.hpp"

DelayedInit<Button> up, enter, down;

void setup() {
  up.init(2, true);
  enter.init(3, true);
  down.init(4, true);
}

void loop() {
  if (up->just_pressed()) {
    ...
  }
  ...
}

感谢 EOF 建议使用展示位置 new