C language mapping
This page describes the C code generated by iopc -l c: how IOP types
map to C types. The runtime APIs that operate on these types are
documented in C runtime library.
1. Generated files
Each IOP package <pkg> produces four files:
<pkg>-t.iop.h-
The C structures corresponding to the IOP types. Never include it directly — include
<pkg>.iop.h. <pkg>-tdef.iop.h-
The C typedefs for the types and their array types, and the enum definitions. Never include it directly either.
<pkg>.iop.h-
The public header: includes the two others and declares the descriptors and helpers.
<pkg>.iop.c-
The type descriptions (reflection data) used by the IOP runtime.
2. Naming conventions
C identifiers are derived by converting CamelCase to snake_case and prefixing with the package name and two underscores:
-
type
FooBarof packagetest→testfoo_bart; -
its descriptor (reflection object) →
testfoo_bars(__efor enum descriptors); -
enum values are prefixed with the upper-snake-case enum name (or the
@prefixvalue):MyEnum.VAL_1→MY_ENUM_VAL_1.
The @ctype(other__t) attribute additionally emits typedef aliases
(and helper-macro aliases) under the given name:
typedef foo__my_enum_a__t e_a__t; /* from @ctype(e_a__t) */
#define e_a_to_str(...) foo__my_enum_a__to_str(__VA_ARGS__)
3. Enums
package test;
enum MyEnum {
VAL_1,
VAL_2,
};
typedef enum test__my_enum__t {
MY_ENUM_VAL_1,
MY_ENUM_VAL_2,
} test__my_enum__t;
Utility helpers are generated/available for conversions, e.g.
iop_enum_to_str(test__my_enum, 1) → "VAL_2" (see lib-common/iop.h
and iop-macros.h).
4. Scalar fields
Mandatory scalars map directly:
| IOP | C | IOP | C |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Optional scalars use opaque option types manipulated through the
OPT_* macros of lib-common/iop-macros.h:
if (OPT_ISSET(st.my_opt_int)) {
printf("set: %d\n", OPT_VAL(st.my_opt_int));
}
5. Strings, bytes, xml
All three map to lstr_t (lib-common/str-l.h). A mandatory
string/bytes/xml must not hold a NULL pointer — use LSTR_EMPTY_V for
the empty value. For an optional field, LSTR_NULL_V (NULL .s
pointer) means absent, which is distinct from empty:
test__my_struct__t foo = {
.a = LSTR_IMMED("plop"), /* mandatory string */
.b = LSTR_NULL_V, /* optional bytes, absent */
};
6. Repeated fields
Arrays are structures with two public fields: tab (pointer to the
elements — pointers to instances for class element types) and len.
Typedefs are provided: iop_array_<type>_t for scalar element types,
<pkg><type>array_t (or IOP_ARRAY_T(pkg__type)) for named types.
The containers are tab_for_each_*-compatible. The runtime never
reallocates or frees an array it did not allocate; emptiness is
len == 0.
7. Struct, union and class fields
-
mandatory struct/union field → inlined value;
-
mandatory reference field (
Type&) → pointer, never NULL once the object is built; -
optional struct/union field → pointer, NULL = absent;
-
class-typed field → always a pointer, whatever the cardinality (NULL = absent for optional fields, invalid-but-transitory for mandatory ones);
-
mandatory
voidfields do not exist in the C struct; optionalvoidfields are a presence boolean.
8. Unions
A union maps to a tagged struct that must be manipulated through the
union macros (lib-common/iop-macros.h):
test__my_union__t u = IOP_UNION_CST(test__my_union, c, LSTR_IMMED("plop"));
IOP_UNION_SWITCH(&u) {
IOP_UNION_CASE_P(test__my_union, &u, a, vp) {
printf("a: %d\n", *vp);
}
IOP_UNION_CASE(test__my_union, &u, b, v) {
printf("b: %jd\n", v);
}
IOP_UNION_CASE(test__my_union, &u, c, v) {
printf("c: %*pM\n", LSTR_FMT_ARG(v));
}
}
IOP_UNION_CASE expands to a for construct: never break
or continue out of an IOP_UNION_SWITCH.
|
Selection and extraction helpers include IOP_UNION_SET,
IOP_UNION_GET, IOP_UNION_CST, …
9. Classes
package test;
class A1 {
static string name = "a1";
int a = 10;
};
class B1 : 1 : A1 {
string b;
};
typedef struct test__a1__t {
const iop_struct_t *__vptr;
int32_t a;
} test__a1__t;
IOP_CLASS(test__a1);
typedef struct test__b1__t {
struct {
const iop_struct_t *__vptr;
int32_t a; /* fields of test__a1__t */
};
lstr_t b;
} test__b1__t;
IOP_CLASS(test__b1);
-
vptrpoints to the descriptor of the instance’s concrete class; never modify it.iop_init(testb1, &v)sets both the defaults and__vptr. -
Parent fields are embedded in a leading anonymous struct, making a child pointer layout-compatible with its ancestors.
-
IOP_CLASS(…)generates the class helpers (structs and unions useIOP_GENERIC(…)). -
Beware of class containers: class fields are pointers, so
iop_initof the container leaves them NULL; mandatory ones must be set before packing.
9.1. Casts and type tests
iop_obj_is_a(ptr, testb1) tests the dynamic type;
iop_obj_vcast(testb2, ptr) / iop_obj_ccast(test__b2, ptr)
(non-const/const) downcast with runtime checking:
if (iop_obj_is_a(a1, test__b2)) {
const test__b2__t *b2 = iop_obj_ccast(test__b2, a1);
...
}
9.2. Switch on dynamic type
IOP_CLASS_SWITCH(name, obj) matches the nearest ancestor case;
IOP_CLASS_EXACT_SWITCH(obj) matches the exact class only. Cases use
IOP_CLASS_CASE(type, obj, var) / IOP_CLASS_CASE_CONST, or plain
case IOP_CLASS_ID(type):. Every IOP_CLASS_SWITCH must contain
IOP_CLASS_DEFAULT(name); IOP_CLASS_EXACT_DEFAULT() is optional for
the exact variant. The switch may only contain classes of the
instance’s inheritance tree.
10. Attribute effects in C
-
@ctype(xt): emits typedef + macro aliases underx(the argument must end int); -
@prefix(A): the enum constants are emitted asA_*(the C type name is unchanged); -
@privateon a field: the field exists in C like any other (privacy is enforced by the encoders and decoders); -
@noReorder: prevents iopc from reordering the C struct members to reduce padding; -
constraint attributes: generate per-field check functions registered in the descriptors (used by
iop_check_constraints, see C runtime library).