【发布时间】:2022-01-08 07:34:33
【问题描述】:
我正在开发一个 arduino 项目,这是相关的,因为 arduino 本身不支持 STL,也不支持动态分配。我注意到我正在编写的很多类在构造上没有任何作用,但是有一个 .init() 方法可以实际初始化任何资源。这是因为这样可以在全局范围内初始化类,然后当 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);
}
这适用于使用init 或begin 方法设计的类。这种设计模式在大多数 Arduino 库中很常见,但对于没有实现它的类,我目前正在使用它作为解决方法:
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 容器类,它将在联合中保存对象所需的内存并处理其生命周期
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;
}
};
这个实现当时被破坏了,因为每当我尝试调用盒装类的任何方法时,它都会锁定 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 实现中存在哪些错误?有没有更好的方法来解决这个问题?
【问题讨论】:
-
展示位置
new()? -
您之前忘记了
return(它本身完全没有任何作用)memory.obj;,但这是最少的问题。模板在需要时忘记构造对象。这是未定义的行为。将某些东西分配给未构造的对象是更加未定义的行为。您基本上需要从 C++17 重新实现std::variant。这是很多代码,要正确执行此操作,比这里显示的要多得多。 -
@SamVarshavchik 感谢您提及退货问题! arduino sdk 中包含的编译器仅支持没有 STL 的 c++11,
std::vairant或std::optional不是一个选项。 -
@EOF Placement new 现在我考虑一下肯定是一个选择。您介意详细说明如何将其集成(或不集成)到
DelayedInit类中吗? -
@Facundo 您可以在已分配的内存上调用placement new,然后您可以使用常规构造函数而不是进行荒谬的扭曲以避免编写理智的构造函数。
标签: c++ memory arduino heap-memory