Before 0.35, C++ implementations of XOD nodes looked like this:
// a special marker to put code in the global scope
{{#global}}
#include <SPI.h>
#include <SD.h>
{{/global}}
// A struct to keep node state.
// Must be declared even if the node is stateless.
struct State {
Number foo;
};
// a special marker between `State` and `evaluate`
{{ GENERATED_CODE }}
void evaluate(Context ctx) {
auto state = getState(ctx); // accessing state struct
state->foo++; // manipulating state
// ...
}
In XOD v0.35, we introduced new implementation syntax that feels more like native C++ and gets rid of a few quirks.
Implementations with old syntax will continue to work.
Let’s take a look at a few cases.
Before 0.35:
// `State` struct must be declared even if it's not used
struct State {};
{{ GENERATED_CODE }}
void evaluate(Context ctx) {
// ...
}
After:
// no separate `State` struct
// no `GENERATED_CODE` marker
node {
void evaluate(Context ctx) {
// ...
}
}
Before:
struct State {
Number foo;
};
{{ GENERATED_CODE }}
void evaluate(Context ctx) {
auto state = getState(ctx); // accessing state struct
state->foo++; // manipulating state
// ...
}
After:
node {
// just like fields in a regular C++ struct
Number foo;
void evaluate(Context ctx) {
// no more `getState`
foo++; // use the fields directly
// ...
}
}
Before:
// all code in the implementation was inside node namespace by default
// a special marker to put code in the global scope
{{#global}}
#include <SPI.h>
#include <SD.h>
{{/global}}
struct State {};
{{ GENERATED_CODE }}
void evaluate(Context ctx) {
// ...
}
After:
// everything outside `node` is global by default,
// no special markers needed
#include <SPI.h>
#include <SD.h>
// explicitly put something in node namespace
nodespace {
static const unsigned char bitmap[] PROGMEM = {/* ... */};
}
node {
Foo foo = Foo(bitmap);
void evaluate(Context ctx) {
// ...
}
}
Before:
struct State {
// `ValueType<output_OUT>::T` was not available
// before GENERATED_CODE marker
// and could not be used to declare state
};
{{ GENERATED_CODE }}
void evaluate(Context ctx) {
ValueType<output_OUT>::T foo = {};
// ...
}
After:
node {
// much simpler syntax,
// available everywhere inside `node` struct
typeof_IN previous = {};
void evaluate(Context ctx) {
typeof_OUT foo = {};
// ...
}
}
Before:
struct State {};
using Type = XColor;
{{ GENERATED_CODE }}
void evaluate(Context ctx) {
Type color = getValue<output_OUT>(ctx);
emitValue<output_OUT>(ctx, color);
}
After:
node {
// Type definition is moved inside `node` and is wrapped in `meta`
meta {
using Type = XColor;
}
void evaluate(Context ctx) {
Type color = getValue<output_OUT>(ctx);
emitValue<output_OUT>(ctx, color);
}
}