C++ 节点 API


C++ 节点 API

本文档描述 C++ 节点中可能会用到的各种数据类型和操作函数。

基本类型

Number

表示一个普通数值。该类型的底层实现为符合 IEEE 754 的 32 位浮点数。

TimeMs

以毫秒表示的时间数值。底层实现为一个 32 位无符号整型数。

XString

表示一个字符串。底层实现为 List<char>,因此也可以使用 List<char> 的对应方法和函数进行操作。

node {} 代码块

每一个 C++ 节点的代码都被包围在 node 块中。每一个 node 块都会被转换为一个 C++ 语言中的结构体(struct)。开发者可以往这个结构体添加属性和方法。

meta {}

使用 meta {} 代码块来封装一些需要被放到一个结构体中的代码。

Context

每一个节点实例的上下文。同一个节点类型的不同实例会对应不同的上下文。每一个 C++ 节点都带这个参数。我们在可视化开发界面看到的输入输出端口对应的参数都会通过 Context 传递。

typeof_$$$

对应到每一个端口的类型。在实现中经常需要使用该方式来进行必要的类型转换。

端口描述

系统会节点的每一个端口都自动生成对应的描述。这种描述不应该被当做类型来实例化变化,而只应该作为模板参数来获取输入输出参数。

以下提到的 $$$ 都会在实际中被替换为端口标签,比如 input_IN1output_FOO 等。

input_$$$

如果一个节点拥有唯一一个没有设置标签的输入端口,那么这个端口会被自动命名为 input_IN。而如果有多个输入端口,则会被命名为 input_IN1input_IN2input_IN3 等等。

output_$$$

每一个 C++ 节点支持不超过 7 个输出端口。

输出端口的命名方式跟输入端口类似,即只有一个输出端口时名称为 output_OUT,多个输出时会被依次命名为 output_OUT1output_OUT2 等,最高到 output_OUT7

端口常量

通常用于静态断言以确认代码的正确性。

constant_input_$$$

常量输入。

constant_output_$$$

节点的常量输出。

通常为手动定义为如下形态:

static constexpr typeof_OUT constant_output_OUT = $$$;

代码里的 $$$ 需要被替换为一个有效的输出值。

如果需要使用到一个结构体中包含的常量,可以定义为如下形态:

static constexpr typeof_OUT constant_output_OUT = typeof_DEV::port;

nodespace {} 代码块

这个代码块用于放置一些不在 struct Node {} 内但又属于节点的代码,因此这些代码只可能被这个节点所访问。

此位置比较适合放置一些 PROGMEM 常量。使用案例可以参考 heart-16x16-rgba 节点。

节点函数

void evaluate(Context ctx)

这是所有 C++ 节点的入口函数。每一个 C++ 节点都需要实现该函数。该函数通常在这类情况下被调用:输入参数变化,超时等等。

ValueT getValue<input_$$$ | output_$$$>(Context ctx)

从输入和输出端口获取相应的最新值。返回的数值类型 ValueT 依赖于该端口的数据类型,可能会为 Numberbool 等等。

需要注意的是,pulse 类型是没有值的,因此没有必要针对 pulse 端口调用此取值函数。如果要知道是否触发了 pulse,应该使用 isInputDirty 方法。

每一个节点掌握输出值的生命周期。因此节点的输出值即可以作为下游节点的输入,也同时可以作为本节点的状态存储。所以,我们在必要时可以使用 getValue<output_$$$>(ctx) 来获取本节点的上一次输出值。

void emitValue<output_$$$>(Context ctx, ValueT value)

设置本节点的输出值。同理,参数类型 ValueT 取决于对应输出端口的数据类型。

对于 emitValue 的调用会导致该节点的下游节点都重新执行求值过程(调用 evaluate() 函数)。需要注意的是,即使本次的输出跟上次输出的值相同,下游节点的求值过程同样会被触发。如果可以的话,建议增加一个检查环节以避免输出同样的值。

如果要触发一个脉冲信号(pulse),请将 value 赋值为 true

bool isInputDirty<input_$$$>(Context ctx)

判断在当前执行周期中某个输入端口是否获取到一个不同的值。是的话返回 true

bool isSettingUp()

只在求值过程第一次被调用时这个函数会返回 true。比较适合在当需要执行一些设备启动时才需要执行的代码时,作为检查手段。

TimeMs transactionTime()

返回自程序启动以来过去的时间长度,单位为毫秒。该函数的返回值在任一求值过程中会保持不变。

void setTimeout(Context ctx, TimeMs timeout)

设置在多长时间后触发一个强制的重新求值过程。

每个节点在同一时刻只能存在一个求值过程。因此后一个求值过程的触发会导致前一个过程被中止。如果我们希望这个新的求值过程紧跟于当前过程之后(而不是中止当前求值过程),那么建议使用 setImmediate 函数。

void clearTimeout(Context ctx)

清理已计划的求值过程。

bool isTimeout(Context ctx)

如果当前求值过程已经超时,则返回 true。从下一次求值过程开始会返回 false

void setImmediate()

强制在本次求值之后马上重启求值。参见 setTimeout

void raiseError<output_$$$>(Context ctx)

往输出端口触发一个错误。对于值类型的端口(非 pulse),这个错误会被一直存储直到输出正常值。而对于 pulse 类型,输出的错误会在下一次求值前就被清理。

bool getError<input_$$$>(Context ctx)

如果该输入端口的上游节点输出了错误,则返回 true,否则返回 false

列表

系统里的列表对象既不是链表也不是向量(vector),而只是对现有数据的一种视图。列表只是创建一个迭代子 Iterator<T> 来遍历整个列表。

使用示例:

Number sum(List<Number> numbers) {
    Number result = 0;
    for (Iterator<Number> it = numbers.iterate(); it; ++it)
        result += *it;
    return result;
}

List<T>

一个类型为 T 的列表。

List<T> 是对 ListView<T> 的一种浅封装。内部实现只是一个指向真实列表数据的指针,因此传值的成本很低。

List<T>::List()

构建一个新的空列表。

List<T>::List(const ListView<T>* view)

将一个 ListView<T> 对象封装为列表对象。

需要注意的是,开发者需要保证 view 对象的生命周期不早于本列表结束,否则会导致程序崩溃。

Iterator<T> List<T>::iterator() const

创建一个新的迭代器,指向第一个元素。

Iterator<T>

迭代器类型。迭代器可以指向一个合格的元素或者指向边界之外,后者用于判断迭代是否结束。

Iterator<T>::operator bool() const

如果指向一个有效元素则返回 true

T Iterator<T>::operator*() const

返回指向的元素。

bool Iterator<T>::value(T* out) const

检测指向的有效性,同时返回指向的元素。

Iterator<T>::operator++()

将当前迭代指向下一个元素。

列表函数

size_t length(List<T> xs)

返回列表长度。这会导致遍历所有元素,因此调用成本较高。

size_t dump(List<T> xs, T* outBuff)

将列表 xs 中的所有元素全部拷贝到 outBuff。使用者需要自行保证缓存的大小足以放下所有的元素,否则会导致程序崩溃。

TR foldl(List<T> xs, TR (*func)(TR, T), TR acc)

对列表使用函数 func 进行 reduce 操作,且以 acc 为初始的累加器。

类型转换

size_t formatNumber(Number value, int prec, char* str)

将一个数值转换为字符串。功能类似于标准库里的 dtostrf / sprintf / to_string,不过性能足够在单片机上运行。

返回转换的字符串长度,未包含结束符。

示例:

valueprecstr
-0.3210"0"
-0.3212"-0.32"
-0.640"-1"
123.4560"123"
123.4562"123.46"
NaNany"NaN"
Infany"Inf"
-Infany"-Inf"
99000000000any"OVF"
-99000000000any"-OVF"
上次编辑于: 2022/12/17 07:45:59
Loading...