Cinf - a C-type-reflection-API and clang plug-in to write reflection metadata.
The Cinf API and plugin enable runtime reflection for the C type system, exposing the following primitives:
- intrinsic, typedef, enum, struct, union, field, array, pointer, constant, function, parameter, attribute, value.
Cinf addresses the following three areas:
- The Cinf reflection graph database format for portable reflection metadata.
- The Cinf clang plug-in outputs C reflection metadata used by the library.
- The Cinf API provides task-oriented query access to C reflection metadata.
This example has an outer-loop iterating through struct types and an inner-loop iterating through struct fields.
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <assert.h>
#include <cinf/model.h>
#include <cinf/db.h>
int main(int argc, const char **argv)
{
if (argc != 2) {
fprintf(stderr, "error: usage: %s <filename>\n", argv[0]);
exit(1);
}
decl_db *db = cinf_db_new();
cinf_db_read_file(db, argv[1]);
size_t ntypes = 0;
cinf_source_decls(cinf_root(db), NULL, &ntypes);
decl_ref *types = calloc(ntypes, sizeof(decl_ref));
assert(types);
cinf_source_decls(cinf_root(db), types, &ntypes);
for (size_t i = 0; i < ntypes; i++) {
size_t nfields = 0;
if (cinf_is_struct(types[i])) {
printf("%s %s : %zu\n",
cinf_tag_name(cinf_decl_tag(types[i])),
cinf_decl_name(types[i]),
cinf_type_width(types[i]));
cinf_struct_fields(types[i], NULL, &nfields);
decl_ref *fields = calloc(nfields, sizeof(decl_ref));
assert(fields);
cinf_struct_fields(types[i], fields, &nfields);
for (size_t j = 0; j < nfields; j++) {
printf("\t%s %s : %zu\n",
cinf_tag_name(cinf_decl_tag(fields[j])),
cinf_decl_name(fields[j]),
cinf_type_width(fields[j]));
}
}
}
cinf_db_destroy(db);
}
Cinf implements a data model based on the description of the C data types in ISO/IEC 9899:9999:
- primary types to model the type system.
- decl node type to model the metadata graph.
- decl node subtypes to model C structures and interfaces:
- intrinsic, typedef, enum, struct, union, field, array, pointer, constant, function, parameter, attribute, value.
Cinf variations from the C standard:
- uses field instead of member for the name of structure elements.
- functions with empty parameter lists are interpreted as functions with no arguments. functions without prototypes are not supported.
The Cinf API uses a small number of primary data types for the reflection graph database, the declaration graph nodes and their properties.
Type | Description |
---|---|
decl_node |
graph database declaration node type |
decl_db |
graph database containing the declarations |
decl_ref |
reference to a single declaration graph node |
decl_tag |
enumeration indicating graph node type |
decl_id |
indice of a graph node in the graph database |
decl_sz |
size type used for array size and bit widths |
decl_set |
type used to indicate many-of set enumerations |
decl_raw |
raw value used to store constants |
The Cinf data structure consists of an array of decl nodes which have a type tag, a set of properties, an interned name, a link to the next item in a list, a link to a child item and a link to an optional attribute list.
Type | Name | Description |
---|---|---|
decl_tag |
tag |
tagged union node type |
decl_set |
props |
type specific properties |
decl_id |
name |
link to node name |
decl_id |
next |
link to next item (south) |
decl_id |
link |
link to child item (east) |
decl_id |
attr |
link to attribute list (south-east) |
The semantic topology of the graph links (south, south-east, east) are used to think about graph layout. On a board, the next node would be south, or below the current node. A child or link node would increment to the east (field type, function parameters, etc). Attributes can exist on any node type and thus are semantically referred to as south-east.
The decl node contains a union with type specific properties such as a count, a width or a size. Not all types use the link field or the quantifer. The type none is used in the link field of an empty struct or union, in the next field to indicate the end of lists, and in the attr field to indicate the absense of attributes.
This table table lists the properties used by each subtype:
Type | Link | Properties | Description |
---|---|---|---|
none |
empty type | ||
typedef |
✓ | alias to another type definition | |
intrinsic |
sz width |
machine type quantified with width in bits | |
enum |
✓ | sz width |
machine type with one-of sequence of integers |
struct |
✓ | sequence of non-overlapping types | |
union |
✓ | sequence of overlapping types | |
field |
✓ | sz width |
named field within struct or union |
array |
✓ | sz count |
sequence of one type |
pointer |
✓ | sz width |
pointer type |
constant |
✓ | sz value |
named constant |
function |
✓ | sz addr |
function with input and output parameter list |
parameter |
✓ | named parameter with link to next | |
qualifier |
✓ | qualifiers for intrinsic types | |
attribute |
✓ | custom attribute name | |
value |
custom attribute value | ||
archive |
✓ | collection of source objects | |
source |
✓ | maps to translation unit | |
alias |
✓ | aliases node overriding its next link |
Cinf contains a work-in-progress experimental model for C linkage that composes the identity of function interfaces and their associated types using Merkle Tree cryptographic hash sums. Merkel Tree style hash sums are composed from the hierachical node properties of an abstract type tree, a portion of the abstract syntax tree containing only the function interfaces and type information. All functions and their associated types are assigned hashes with the following constraints and properties:
- type hashes include information required to precisely identify types.
- type hashes are composed hierarchically in a deterministic traveral order recording successor and adjacency based on hash sum absorbtion order.
- type hashes are position invariant, but order preserving.
- type hashes absorb names but reference dependent nodes using hash sums.
- identical declarations with different ancestors have identical hash sums.
- types and interfaces link to dependencies without knowing their names.
- type hashes for incomplete types have different sums to complete types.
- semicolon is used as a delimeter as it does not occur in type names.
- SHA-224 is used because it is not subject to length extension attacks.
This example invocation of cinftool
shows the extended reflection meta-data
generated with ./scripts/run_tests.py --dump-all test/adjacent-decls-3.h
:
The Cinf implementation is currently alpha software.
- binary format is subject to change and needs to be more compact.
- format was reduced ~20% in size by eliding builtin types.
- format could be made even smaller using LEB128 or ASN.1.
- C intrinsic data types.
- integer types.
- bit, byte, short, int, long, cent
- sign, ubyte, ushort, uint, ulong, ucent
- floating point types.
- half, float, double, quad
- complex number types.
- chalf, cfloat, cdouble, cquad
- boolean type.
- bool
- vector types.
- vec2, vec3, vec4, ...
- matrix types.
- mat2x2, mat3x3, mat4x4, ...
- integer types.
- nested struct, union, field and intrinsic types.
- bitfield widths.
- arrays and pointers.
- VLA size expressions.
- typedef type aliases.
- enum and enum constants.
- functions and function parameters.
- const, volatile and restrict.
- static, inline, extern.
- function addresses.
- attributes (
__attribute__
).alias(
"symbol")
aligned(
alignment)
always_inline
annotate(
"string")
deprecated
packed
pure
used
unused
cinf has been tested on ubuntu 20.04 LTS.
ubuntu 20.04 LTS:
sudo apt-get install llvm libclang-dev
ubuntu 20.04 LTS:
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build build -- --verbose
to run the cinf plugin and dump the reflection table to stdout:
clang test/simple-struct-1.h \
-Xclang -load -Xclang build/cinfcc.so \
-Xclang -plugin -Xclang cinfcc \
-Xclang -plugin-arg-cinfcc -Xclang -dump
to run the cinf plugin and write the reflection data to a file:
clang test/simple-struct-1.h \
-Xclang -load -Xclang build/cinfcc.so \
-Xclang -plugin -Xclang cinfcc \
-Xclang -plugin-arg-cinfcc -Xclang -o \
-Xclang -plugin-arg-cinfcc -Xclang tmp/simple-struct-1.refl
to enable cinf plugin debugging, add the following option:
-Xclang -plugin-arg-cinfcc -Xclang -debug