CX Framework
Cross-platform C utility framework
Loading...
Searching...
No Matches
Struct System

Macros

#define structInitMany(structname, s, n)    _structInitMany(STRUCTBASE(s), &structInfoName(structname), n)
 
#define structInit(structname, s)   _structInitMany(STRUCTBASE(s), &structInfoName(structname), 1)
 
#define structCreate(structname)   ((structname*)_structAlloc(&structInfoName(structname)))
 
#define structDestroyMembersMany(s, n)   _structDestroyMembersMany(STRUCTBASE(s), n)
 
#define structDestroyMembers(s)   _structDestroyMembersMany(STRUCTBASE(s), 1)
 
#define structDestroy(ps)   _structDestroy(STRUCTHANDLE(ps))
 

Enumerations

enum  StructMemberFlagsEnum { STRUCT_NoDestroy = 1 << 0 , STRUCT_NoCopy = 1 << 1 , STRUCT_NoSerialize = 1 << 2 , STRUCT_Ignore = STRUCT_NoDestroy | STRUCT_NoSerialize | STRUCT_NoCopy }
 

Functions

const StructInfo * structSetFind (const StructSet *ss, strref name)
 

Detailed Description

The CX struct system provides plain-old-data (POD) C structures with runtime type introspection, automatic serialization, and lifecycle management. Unlike objects in the CX object system, structs are not reference counted, are not polymorphic, and carry no vtable overhead — they are simply annotated C structs with metadata automatically generated from .cxh definitions.

Key Features

Defining Structs in .cxh Files

Structs are declared in .cxh files using the struct keyword:

struct Point {
float64 x;
float64 y;
}
struct Config {
[serializeas server-name] string hostname; // written as "server-name"
[default 8080] uint16 port; // starting value, see below
[noserialize] string cachedPassword; // excluded from serialization
}

The build system (via add_cxautogen() in CMakeLists.txt) generates:

Never edit generated .h files directly — modify the .cxh source instead.

[default value] gives a member a starting value other than zero: structInit() and structCreate() copy it in after the zero-fill, so cfg.port above comes out 8080 rather than 0. It also feeds serialization — see the Serialization section below.

Memory Layout

Every generated struct type begins with a StructBase union as its first member:

typedef struct Point {
union {
StructInfo* structinfo;
void* _is_Struct; // type-check marker
};
float64 x;
float64 y;
} Point;

This overlay means a Point* is castable to StructBase* for generic handling, and the structinfo pointer is always available at a known, zero offset.

Allocation Strategies

Structs are not restricted to the heap:

// Stack allocation — zero-init manually or via structInit()
Point p = { 0 };
structInit(Point, &p);
p.x = 1.0;
p.y = 2.0;
structDestroyMembers(&p); // clean up members, struct itself is on the stack
// Heap allocation
Config *cfg = structCreate(Config);
cfg->port = 8080;
structDestroy(&cfg); // destroys members and xaFree()s the pointer
// Flat array (inline storage, not pointer-to-struct) — manual memory management
Config *arr = xaAlloc(32 * sizeof(Config));
structInitMany(Config, &arr[0], 32);
// ... use arr ...
xaFree(arr);
#define structInitMany(structname, s, n)
Definition struct.h:99
#define structDestroyMembers(s)
Definition struct.h:185
#define structInit(structname, s)
Definition struct.h:118
#define structDestroyMembersMany(s, n)
Definition struct.h:165
#define structCreate(structname)
Definition struct.h:139
#define structDestroy(ps)
Definition struct.h:205
void xaFree(void *ptr)
#define xaAlloc(size,...)
Definition xalloc.h:199

Lifecycle Functions

Function Frees memory? Description
structInit(Type, ptr) No Set metadata pointer; zero-fill members
structDestroyMembers(ptr) No Destroy members via stype dtors
structDestroyMembersMany(ptr, n) No Same, for a flat array of n structs
structDestroy(&ptr) Yes structDestroyMembers + xaFree; sets NULL

Custom init and destroy hooks can be declared in the .cxh file (analogous to object system init()/destroy()):

struct Config {
string hostname;
uint16 port;
init(); // called after zero-fill and structinfo setup
destroy(); // called before automatic member cleanup
}

SType Descriptors

A generated struct type works as a stype token exactly like any built-in type: pass the struct's name wherever a type argument is expected, including as a container element type.

sa_Point pts;
saInit(&pts, Point, 64);
Point p = { 0 };
structInit(Point, &p);
p.x = 1.0; p.y = 2.0;
saPush(&pts, Point, p);
#define saInit(out, type, capacity,...)
Definition sarray.h:315
#define saPush(handle, type, elem,...)
Definition sarray.h:460

Heap-allocated struct pointers use a second, separate token: structp. It takes no type parameter — every heap struct carries its own StructInfo* at offset 0, so structp reads the concrete type back from the pointer itself. This makes containers of structp values heterogeneous automatically, and whatever a slot holds, the container destroys it (via structDestroy) when the slot is overwritten or the container itself is destroyed.

htInsert() copies the struct in, like it does for any type — the pointer you passed is still yours to destroy. To move an existing heap struct into the container without a copy, steal it with htInsertC() instead:

hashtable ht;
htInit(&ht, string, structp, 16);
Config *cfg = structCreate(Config);
htInsertC(&ht, string, _SL("main"), structp, &cfg); // moves cfg into ht; cfg is now NULL
Point *pt = structCreate(Point);
htInsertC(&ht, string, _SL("origin"), structp, &pt); // a different struct type, same table
#define htInit(out, keytype, valtype, initsz,...)
Definition hashtable.h:344
#define htInsertC(htbl, ktype, key, vtype, val,...)
Definition hashtable.h:470
#define _SL(s)
Inline ASCII string literal with compile-time embedded length (STR_LEN8). Content must be < 200 bytes...
Definition strliteral.h:207

Slots That Can Hold More Than One Struct Type

A struct set is a named, sorted list of struct types — the declared vocabulary for a slot that may hold any one of them. Declare it with structset in a .cxh file and type a structp member by it:

struct Circle { struct[Point] center; float64 radius; }
struct Rect { struct[Point] topLeft; struct[Point] bottomRight; }
structset ShapeSet { Circle, Rect }
struct Drawing {
structp[ShapeSet] shape; // any struct in ShapeSet
}

structp[ShapeSet] behaves like bare structp at runtime — one heterogeneous pointer, dispatched through the pointee's own StructInfo*. Serialization is where the set matters: writing a struct that isn't in ShapeSet fails, and reading a Drawing resolves the concrete shape by name against ShapeSet with nothing to configure on the reader. Look a type up directly with structSetFind().

Serialization

Structs serialize through the generic Serialization module, using the struct's generated schema (stExt(StructName)) and type (stType(StructName)) — there is no struct-specific serialization code, the same traverser and backends handle every stype-described value.

Config *cfg = structCreate(Config);
cfg->port = 8080;
string json = 0;
StreamBuffer *sb = sbufStrCreatePush(&json, 4096);
serWrite(w, Config, *cfg);
Config *cfg2 = structCreate(Config);
sb = sbufCreate(4096);
SerReader *r = serJsonReaderCreate(sb, 0);
serRead(r, Config, cfg2);
strDestroy(&json);
@ SER_JSON_Pretty
4-space indent
Definition sertype.h:158
SerReader * serJsonReaderCreate(StreamBuffer *sb, flags_t flags)
SerWriter * serJsonWriterCreate(StreamBuffer *sb, flags_t flags)
void serReaderDestroy(SerReader **r)
#define serRead(r, type, pval)
Definition serreader.h:192
#define sbufCreate(targetsz,...)
Definition streambuf.h:275
void sbufFinish(StreamBuffer **sb)
StreamBuffer * sbufStrCreatePush(string *strout, size_t targetsz)
bool sbufStrPRegisterPull(StreamBuffer *sb, strref str)
bool serWriterFinish(SerWriter *w)
#define serWrite(w, type, val)
Definition serwriter.h:188
void serWriterDestroy(SerWriter **w)
void strDestroy(strhandle ps)

The same calls work unchanged against the binary or SSD-tree backend — see Serialization for the full read/write API, the available backends, and how object classes serialize.

A member declared [default value] is omitted from the document entirely when it still holds that value, and a document that omits it reads back as that value — SER_EmitDefaults writes it anyway. This is automatic once the member is annotated; there is nothing to opt into at the call site.

[noserialize] drops a member from the wire entirely. [serializeas X] keeps the member but changes the name it goes out under: the C identifier is unaffected, and X is the only spelling a document may use for it.

Naming Conventions

Macro Definition Documentation

◆ structCreate

#define structCreate (   structname)    ((structname*)_structAlloc(&structInfoName(structname)))

struct* structCreate(structname);

Allocates and initializes a single struct instance of the given type on the heap.

Allocates memory for the struct, zero-fills it, and calls the type's custom init function (if any) to set up non-zero default values. The returned pointer must eventually be freed with structDestroy().

Parameters
structnameName of the struct type (without the struct keyword)
Returns
Pointer to the newly allocated and initialized struct instance

Example:

MyStruct *s = structCreate(MyStruct);
// ... use s ...

Definition at line 139 of file struct.h.

◆ structDestroy

#define structDestroy (   ps)    _structDestroy(STRUCTHANDLE(ps))

void structDestroy(struct** ps);

Destroys and frees a heap-allocated struct instance.

Calls the custom destructor (if any), releases all managed members (strings, containers, objects, etc.), frees the heap memory, and sets the pointer to NULL. The struct must have been allocated with structCreate().

Parameters
psPointer to the struct pointer to destroy; set to NULL on return

Example:

MyStruct *s = structCreate(MyStruct);
// ... use s ...
structDestroy(&s); // s is NULL after this

Definition at line 205 of file struct.h.

◆ structDestroyMembers

#define structDestroyMembers (   s)    _structDestroyMembersMany(STRUCTBASE(s), 1)

void structDestroyMembers(struct* s);

Destroys the members of a single struct instance without freeing the struct.

Calls the custom destructor (if any) and then releases all managed members (strings, containers, objects, etc.). The struct memory itself is not freed — use this for stack-allocated or embedded structs. For heap-allocated structs, use structDestroy() instead.

Parameters
sPointer to the struct instance whose members should be destroyed

Example:

MyStruct s;
structInit(MyStruct, &s);
// ... use s ...

Definition at line 185 of file struct.h.

◆ structDestroyMembersMany

#define structDestroyMembersMany (   s,
 
)    _structDestroyMembersMany(STRUCTBASE(s), n)

void structDestroyMembersMany(struct* s, int n);

Destroys the members of n consecutive struct instances without freeing the structs.

Calls the custom destructor (if any) and then releases all managed members (strings, containers, objects, etc.) of each struct. The struct memory itself is not freed — use this for stack-allocated or embedded structs.

Parameters
sPointer to the first struct instance whose members should be destroyed
nNumber of consecutive struct instances to process

Example:

MyStruct arr[4];
structInitMany(MyStruct, &arr[0], 4);
// ... use arr ...

Definition at line 165 of file struct.h.

◆ structInit

#define structInit (   structname,
 
)    _structInitMany(STRUCTBASE(s), &structInfoName(structname), 1)

void structInit(structname, struct* s);

Initializes a single struct instance of the given type.

Zero-fills the struct and then calls the type's custom init function (if any) to set up non-zero default values. The struct memory must already be allocated — this function only initializes its contents.

Parameters
structnameName of the struct type (without the struct keyword)
sPointer to the struct instance to initialize

Example:

MyStruct s;
structInit(MyStruct, &s);

Definition at line 118 of file struct.h.

◆ structInitMany

#define structInitMany (   structname,
  s,
 
)     _structInitMany(STRUCTBASE(s), &structInfoName(structname), n)

void structInitMany(structname, struct* s, int n);

Initializes n consecutive struct instances of the given type.

Zero-fills each struct and then calls the type's custom init function (if any) to set up non-zero default values. The struct memory must already be allocated — this function only initializes its contents.

Parameters
structnameName of the struct type (without the struct keyword)
sPointer to the first struct instance to initialize
nNumber of consecutive struct instances to initialize

Example:

MyStruct arr[4];
structInitMany(MyStruct, arr, 4);

Definition at line 99 of file struct.h.

Enumeration Type Documentation

◆ StructMemberFlagsEnum

Enumerator
STRUCT_NoDestroy 

Member should not be automatically destroyed.

STRUCT_NoCopy 

Member should be skipped during copy operations.

STRUCT_NoSerialize 

Member should not be serialized (e.g. by JSON, etc.)

STRUCT_Ignore 

Combines all of the above; ignored members may also be left out of the member table entirely, e.g. for a type the stype system doesn't know about.

Definition at line 16 of file struct.h.

Function Documentation

◆ structSetFind()

const StructInfo * structSetFind ( const StructSet *  ss,
strref  name 
)

Looks up a struct type by name in a StructSet using binary search.

Parameters
ssThe StructSet to search (must have entries sorted by name)
nameThe struct type name to look up
Returns
Pointer to the matching StructInfo, or NULL if not found