C++ Code

LayoutEditor supports macros in a C / C++ style. The C/C++ style is very similar to standard C / C++, but not all syntax is supported. These syntax will work in LayoutEditor macros:

Datatypes

bool boolean datatype, can be true or false

int signed integer, 32bit range (-2147483648 to 2147483647)

long or int64 will create a integer with 64bit range

double double precision float, +/- 1.7e +/- 308 (~15 digits)

float identical with double

string as an object, please see class string

int i=0;
double d=0.45;
double f=1E10;
bool b=false;

Type conversion is implicite done:

d=10.0;
i=d;
d=i;
string s=d; // s is set to "10"
s=s+"5"; // s is set to "105"
i=s; // i is set to 105

arrays The datatypes int, double and string can be used as array. The resulting variables are identical to the classes stringList, intList and doubleList.

string sl[4];

sl[0]="string at pos 0";
sl[2]="a further stringstring";
sl[3]="another string";

sl.clear();
sl.append("one more string");

// sl will be a stringList with the size 1

Control Structures

for (start condition;stop condition;step command){commands; beak; continue;}

while (condition) {commands; break; continue;}

do {commands; break; continue; } while (condition)

if (condition) {commands} else {commands} // else is optional

return parameter // leaving the subfunction

switch (parameter) { case parameter: command break; default: command }

break and continue are supported. A break will end the loop. A continue will directly start the next loop iteration.

int i,k;

for (i=0;i<10;i++){ k=k+5;}

while (k>10) {k=k/2;}

do { k++; } while (k<20);

if (i+k==23) k=k*5;

switch (i) {
case 0: i=4;
 break;
default: i=0;
}

Objective Commands

pointers are supported as in C++, however a new and delete will not work. Please use the class methods to generate new pointer objects. New classes cannot be defined. Just the shipped classes can be used. Conventional function definition can be used in macros as long it is defined before the main function.

int sqr( int i){
return i*i;
}

int main(){
int k=sqr(5);
point p(4,5);

}

Compiler Commands

Two ways to insert comments:

// one line comment

/* multi line
comment */

to include another file to the macro use:

#include "filename"

Including standard C/C++ header files will not link its standard C/C++ library. To use standard C/C++ extensions, please use its associated classes like math.

Alle classes from the API documention are availabale without any include statement. So only include your own macro files with additional code.

Debugging

debug(varName1,varName2,...); will write the variable information to the debug output,

debug.clear(); will clear the debug output,

debug.show(); will open a text editor with the debug output,

debug.saveTo("filename"); will save the debug output to filename,

debug.console(true); will also output debug information to the console, works on Linux and Mac OS X only,

Debugging is global and debug output will remain in the memory after macro termination. So it can be display with a further macro until the LayoutEditor is terminated or the debug output is cleared.

Starting with release 20190807 also a cout("mytext"); can be used to generate a console output.

C++ language since Version 20260918

Everything from here on is available starting with version 20260918. Older LayoutEditor releases will not compile these macros. The restrictions in the sections above (no own classes, functions only before main, #include does not load a C library) apply only to those older versions.

In short, a macro can now use a much larger part of everyday C++:

  • preprocessor: #include <libname>, #include "file", #define, #undef, #ifdef, #ifndef, #else, #endif
  • own data types: struct and class
  • several functions with the same name (overloading) and default argument values
  • references (int&) and a wider set of pointers (int *, &x, p->field)

You still do not need a full C++ compiler. The LayoutEditor reads the macro itself. Not every C++ feature exists (see the last section). The examples below are written so you can copy them even if you do not write C++ every day.

Preprocessor (#include, #define, #ifdef)

Lines that start with # are handled before the rest of the macro is compiled. They do not draw anything; they prepare the source text.

#include "filename" — insert another macro file

This is the include that already existed. The other file is copied into this macro as source code. Use it to share helper functions between macros.

#include "helpers.layout"

int main(){
 int k = includedAdd(2, 3);
}

#include <libname> — load a C library plugin

From version 20260918, angle brackets mean something different from quotes. #include <math.h> does not open /usr/include/math.h and does not paste C header text into the macro. It loads a LayoutEditor plugin that registers the matching C functions and constants.

If the plugin is missing, compilation stops with an error. There is no silent fallback to a system header.

The LayoutEditor classes (layout, point, cell, math, …) still need no include. You only add #include <…> when you want the C-style library functions (sin, fopen, …).

Bundled libraries (C and C++ names are aliases of the same plugin):

Write this What you get (typical)
#include <math.h> or <cmath> sin, cos, sqrt, pow, fabs, M_PI, …
#include <stdlib.h> or <cstdlib> atoi, atof, abs (no malloc / exit)
#include <stdio.h> or <cstdio> FILE *, fopen, fputs, fgets, fclose (no printf)
#include <ctype.h> or <cctype> isdigit, isalpha, toupper, tolower (pass character codes, e.g. 65 for 'A')
#include <time.h> or <ctime> time, localtime, strftime
#include <string.h> memcmp (not strcpy / strcat; use the string class for text)
#include <stdint.h> / <stddef.h> / <stdbool.h> INT32_MAX, NULL, …
#include <errno.h> or <cerrno> get_errno, set_errno, EDOM, …
#include <zlib.h> zlib helpers such as compressText / uncompressText

Each binding has its own page with a full function list and an example under Extern API.

#include <math.h>

int main(){
 double quarterCircle = sin(M_PI / 2.0); // 1.0
 double side = sqrt(4.0); // 2.0
 cout("sin(pi/2) = ", quarterCircle, "\n");
}

The built-in class math still works without an include (math.sin(x)). #include <math.h> adds the C names (sin(x), M_PI) on top.

You can add further libraries as plugins in ~/LayoutEditor/plugins (Windows: %USERPROFILE%\LayoutEditor\plugins). The include name is the library header, for example #include <zlib.h>.

#define, #undef, #ifdef — names and conditional code

#define NAME records that NAME exists. #define NAME 10 also replaces later occurrences of NAME with 10 (like a named constant). #undef NAME forgets it again.

#ifdef NAME … #endif keeps the enclosed lines only if NAME was defined. #ifndef is the opposite. #else chooses the other branch. You can nest these blocks.

#define FEATURE
#define WIDTH 20

#ifdef FEATURE
int extra = WIDTH; // extra becomes 20
#endif

#ifndef OLD_ENGINE
int useNewPath = 1;
#else
int useNewPath = 0;
#endif

int main(){
 return extra; // 20
}

Useful details:

  • #define FEATURE with no value is only a switch for #ifdef. It does not delete the word FEATURE from the rest of the file.
  • #define WIDTH 20 does replace WIDTH by 20 everywhere (except inside strings and comments).
  • Function-style macros such as #define MAX(a,b) … are not supported. Write a real function instead.
  • #if WIDTH > 10 is not supported. Only #ifdef / #ifndef (is this name defined or not).
  • #elif is not supported; use nested #ifdef / #else.

#parameter, #parameterInt, #parameterDouble, #parameterBool and #parameterString still declare parametric-cell inputs, as before. They can sit next to #ifdef so a parameter is only defined for a certain build.

User-defined Structs

A struct is a named bundle of values that belong together — like a form with several fields. Instead of three loose variables name, age, salary, you keep one Person.

From version 20260918 you can define such types yourself. Allowed field types are int / long / char, double / float, bool, string (or std::string), pointers, and other structs/classes you defined. long and char are stored as int; float is stored as double.

struct Person {
 string name;
 int age;
 double salary;
};

int main(){
 // Fill the fields in the order they were declared.
 Person p = {"Anna", 28, 4500.50};

 p.age = 29; // change one field
 string n = p.name; // read a field → "Anna"

 Person copy = p; // copy all fields
 copy.name = "Ben"; // p.name is still "Anna"
}

The list in { … } fills the fields from top to bottom. If you leave values off at the end, they stay at their default: 0, 0.0, false or "".

A field can itself be another of your types. That inner object is created automatically. Changing box.counter.n changes the original, not a throw-away copy. Copying box copies the inner object as well.

struct Counter {
 int n;
};

struct Box {
 Counter counter; // a Counter lives inside every Box
 int w;
};

int main(){
 Box b; // b.counter.n starts at 0, b.w at 0
 b.counter.n = 5;
 Box other = b; // other.counter.n is 5, independent of b
}

A field may also be a pointer to another object (Counter *p;). A pointer to the type you are currently defining is allowed (Node *next; inside Node) — that is how a linked list is written. Putting a full Node inside Node is an error (that would nest forever).

Limits: you cannot make an array of structs (Person list[10];), and you cannot use { … } initialization if the struct contains another struct instance. Do not reuse a built-in class name (point, layout, …) for your type.

User-defined Classes

A class is a struct plus functions that belong to that data. Those functions are called methods. You call them on an object: v.scale(2.0) means “run scale on this v”.

From version 20260918 you can define your own classes. struct and class use the same syntax: both may have fields and methods. There is no inheritance (a class cannot extend another class).

The special function with the same name as the class is the constructor. It runs when you create an object and sets the starting values.

class Vec {
public:
 // constructors: how a new Vec is set up
 Vec() : x(0), y(0) {}
 Vec(double x, double y) : x(x), y(y) {}

 void scale(double f){
 x = x * f;
 y = y * f;
 }
 double len2(){
 return x*x + y*y;
 }
private:
 double x;
 double y;
};

int main(){
 Vec a; // uses Vec() → x=0, y=0
 Vec v(3.0, 4.0); // uses Vec(double, double)
 v.scale(2.0); // now x=6, y=8
 double d = v.len2(); // 6*6 + 8*8 = 100
}

How to read the pieces:

  • Vec v(3.0, 4.0); creates a Vec and calls the matching constructor. Vec(3.0, 4.0) can also stand alone as an expression.
  • The list after the colon (: x(x), y(y)) is the initializer list. It runs before the constructor body. Each entry means “set this field”. If a parameter has the same name as the field, x(x) still sets the field from the parameter, as in C++. y() with empty parentheses keeps the default the object was created with.
  • Inside a method you may write the field name (x = k) or this.x. this means “the object this method was called on”. You may also call another method of the same class by name (len2()), which is the same as this.len2().
  • public: members can be used from main and from other functions. private: and protected: members can only be used inside the class itself. Using v.x from main is a compile error if x is private.
  • Unlike desktop C++, both struct and class start as public. A type written without public: / private: therefore keeps working.

Not available: inheritance (class Sub : public Base), a class nested inside another class, destructors, virtual, static members, const methods, operator overloading (Vec operator+), friend, new and delete.

Overloading (same name, different arguments)

From version 20260918 several functions may share one name. That is called overloading. When you write f(1, 2), the LayoutEditor looks at how many values you passed (and, if needed, their types) and picks the matching declaration.

You no longer have to invent names like createCircleXY and createCirclePoint. One name createCircle is enough:

void createCircle(int x, int y){ /* two coordinates */ }
void createCircle(point p){ /* one point */ }
void createCircle(point p, int layer){ /* point plus layer */ }

int f(){ return 10; }
int f(int a){ return 20 + a; }
int f(int a, int b, int c = 7){ return 30 + a + b + c; }

int main(){
 createCircle(1, 5); // two ints → first function
 point pa;
 createCircle(pa); // one point → second function
 createCircle(pa, 0); // point+int → third function

 int a = f(); // 10
 int b = f(1); // 21
 int c = f(1, 2); // 40 (c defaults to 7)
}

The last f shows a default argument: int c = 7 may be omitted at the call. f(1, 2) is then the same as f(1, 2, 7). Default values work for free functions (functions that do not belong to a class). Methods and constructors do not fill in missing arguments from defaults; you must pass every parameter they declare, or provide another overload with fewer parameters.

If two declarations have the same number of parameters, the types decide: createCircle(1, 5) is (int, int), createCircle(pa, 0) is (point, int). Types are taken from things the compiler can see (numbers, typed variables, point(...), your structs). If a type is unknown, that candidate is not rejected. If two candidates still look equal, the first one in the file wins.

Writing the same name, the same number of parameters and the same parameter types twice is an error.

Overloading works for free functions, for methods (v.area() vs v.area(2)) and for constructors (Box b; vs Box b(4); vs Box b(3, 5);).

Functions, methods and constructors no longer have to sit above main. All names are collected first, then the bodies are compiled. A method may therefore call another method that is written further down in the same class.

References (Type&)

A normal function parameter is a copy. If the function changes x, the caller’s variable stays the same.

A reference parameter int& x is a nickname for the caller’s variable. Changes inside the function are visible afterwards. From version 20260918 user functions may use this.

You do not have to write & at the call. bump(a) is enough; the LayoutEditor passes the original a.

void bump(int& x){
 x = x + 1; // changes the caller’s variable
}

void setName(string& s){
 s = "Metal1";
}

int main(){
 int a = 7;
 bump(a); // a is now 8

 string layer = "";
 setName(layer); // layer is now "Metal1"
}

Type& works on free functions only, not on methods of your classes. For a method, pass a pointer (Type *) instead, or return the new value.

Pointers (Type *, &, ->)

A pointer is not the value itself; it is the address of a variable. Think of a house number: it tells you where to find the house.

  • int *p; — p will hold the address of an int
  • &x — “address of x”
  • *p — “the int that lives at that address”
  • p->field — “the field named field of the object p points to” (same as (*p).field)

The LayoutEditor already used pointers for its own classes (layout->drawing). From version 20260918 you can use the same idea for your own variables, arrays and structs.

int solve(double *root){
 *root = 1.5; // write into the caller’s variable
 return 1;
}

void setFirst(int *p){
 p[0] = 9; // p points at an array
}

struct Pair {
 int a;
 int b;
};

void setA(Pair *obj){
 obj->a = 5;
}

int main(){
 double r;
 solve(&r); // pass the address of r; r becomes 1.5

 double target = 0;
 double *p = &target;
 *p = 9.5; // target is 9.5
 (*p)++; // target is 10.5 (steps the value, not the address)

 int a[3];
 a[0] = 1; a[1] = 2; a[2] = 3;
 setFirst(a); // arrays decay to a pointer; a[0] is 9
 int *q = &a[1]; // address of the second element
 *q = 7; // a[1] is 7

 Pair pr;
 setA(&pr); // pr.a is 5
 Pair *sp = &pr;
 sp->a = 6; // same object; pr.a is 6

 if (sp == NULL) return 1; // false: sp points at pr
}

What works:

  • function parameters Type *p and local pointers; *p reads, *p = v writes through
  • (*p)++, ++(*p), (*p)-- change the value pointed to
  • passing an array a to int *p or int p[] — p[i] reads and writes the caller’s array
  • &a[i] — pointer to one array element
  • pointers to your structs/classes: Pair *p = &pr, p->a, p->method(), returning Pair*
  • pointer fields (double *p;, Counter *c;), which start as NULL until you assign them
  • a cast such as (double*)&x
  • passing a pointer or *p into a Type& parameter keeps the write-back

A pointer that is NULL or was never assigned must not be used with *p or p->…. That is a runtime error.

Still not supported: new / delete, moving a pointer by arithmetic (p+1, p++), sizeof, pointers to functions, and pointers to pointers (int **pp, &p when p is already a pointer).

What is still not C++

The language is C++-like, not a complete C++ compiler. Besides the limits already named:

  • no new / delete — create objects as locals (Vec v(1,2);) or use the LayoutEditor class methods that return pointers
  • no inheritance and no class nested inside another class
  • no C++ templates and no namespaces. Spellings that look like them are only aliases: std::string is the same as string, std::vector<int> is the same as intList (an int array)
  • no #if expressions and no function-like #define
  • #include <lib.h> only works if the matching LayoutEditor plugin is installed; it never compiles a system C header as-is