Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,143 changes: 1,667 additions & 1,476 deletions distr/flecs.c

Large diffs are not rendered by default.

461 changes: 259 additions & 202 deletions distr/flecs.h

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions examples/c/reflection/custom_serializer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.bake_cache
.DS_Store
.vscode
gcov
bin
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef CUSTOM_SERIALIZER_H
#define CUSTOM_SERIALIZER_H

/* This generated file contains includes for project dependencies */
#include "custom_serializer/bake_config.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifdef __cplusplus
}
#endif

#endif

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
)
(.)
.|.
| |
_.--| |--._
.-'; ;`-'& ; `&.
\ & ; & &_/
|"""---...---"""|
\ | | | | | | | /
`---.|.|.|.---'

* This file is generated by bake.lang.c for your convenience. Headers of
* dependencies will automatically show up in this file. Include bake_config.h
* in your main project file. Do not edit! */

#ifndef CUSTOM_SERIALIZER_BAKE_CONFIG_H
#define CUSTOM_SERIALIZER_BAKE_CONFIG_H

/* Headers of public dependencies */
#include <flecs.h>

#endif

9 changes: 9 additions & 0 deletions examples/c/reflection/custom_serializer/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "custom_serializer",
"type": "application",
"value": {
"use": [
"flecs"
]
}
}
332 changes: 332 additions & 0 deletions examples/c/reflection/custom_serializer/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,332 @@
#include <custom_serializer.h>
#include <stdio.h>
#include <inttypes.h>

// This example demonstrates how to create a serializer for a custom format.

static int indent = 0;

#define printIndented(...)\
printf("\n%*s", indent * 2, ""); printf(__VA_ARGS__)

void serializeOps(ecs_world_t *world, ecs_meta_op_t *ops,
int32_t op_count, const void *ptr);

// Serialize a pointer of a specified type.
int serialize(ecs_world_t *world, ecs_entity_t type, const void *ptr) {
// The TypeSerializer component contains a vector of instructions that tells
// us how to serialize the type.
const EcsTypeSerializer *s = ecs_get(world, type, EcsTypeSerializer);
if (!s) {
printf("type does not have reflection data\n");
return -1;
}

serializeOps(world, ecs_vec_first(&s->ops), ecs_vec_count(&s->ops), ptr);

return 0;
}

// Serialize contents of struct or collection scope
void serializeScope(ecs_world_t *world, ecs_meta_op_t *ops, const void *ptr) {
// A scope starts with a Push and ends with a Pop, so trim the first and
// last instruction before forwarding.
serializeOps(world, ops + 1, ops->op_count - 2, ptr);
}

// Serialize a struct scope
void serializeStruct(ecs_world_t *world, ecs_meta_op_t *ops,
const void *ptr)
{
printf("{");
indent ++;

serializeScope(world, ops, ptr);

indent --;
printIndented("}");
}

// Serialize an array scope
void serializeArray(ecs_world_t *world, ecs_meta_op_t *ops,
int32_t elem_count, const void *ptr)
{
printf("[");

// Iterate elements of the array. Skip the first and last instruction
// since they are PushStruct and Pop.
for (int i = 0; i < elem_count; i ++) {
if (i) {
printf(", ");
}

serializeScope(world, ops, ptr);

ptr = ECS_OFFSET(ptr, ops->elem_size);
}

printf("]");
}

// Serialize a vector scope
void serializeVector(ecs_world_t *world, ecs_meta_op_t *ops,
const void *ptr)
{
const ecs_vec_t *vec = ptr;
serializeArray(world, ops, vec->count, vec->array);
}

// Serialize enum
void serializeEnum(ecs_meta_op_t *op, const void *ptr)
{
const int32_t *value = ptr; // Assume i32, but should use underlying type
ecs_enum_constant_t *c = ecs_map_get_deref(op->is.constants,
ecs_enum_constant_t, (ecs_map_key_t)*value);
printf("%s", c->name);
}

// Iterate over instruction array
void serializeOps(ecs_world_t *world, ecs_meta_op_t *ops, int32_t op_count,
const void *base)
{
for (int i = 0; i < op_count; i ++) {
ecs_meta_op_t *op = &ops[i];

// Member name
if (op->name) {
if (i) {
printf(",");
}

printIndented("%s: ", op->name);
}

// Get pointer for current field
void *ptr = ECS_OFFSET(base, op->offset);

switch(op->kind) {
// Instructions that forward to a type scope, like a (nested) struct or
// collection
case EcsOpPushStruct:
serializeStruct(world, op, ptr);
break;
case EcsOpPushArray:
serializeArray(world, op, ecs_meta_op_get_elem_count(ops, ptr), ptr);
break;
case EcsOpPushVector:
serializeVector(world, op, ptr);
break;

// Opaque types have in-memory representations that are opaque to
// the reflection framework and cannot be serialized by just taking
// a pointer + an offset. See src/addons/script/serialize.c for an
// example of how to handle opaque types.
case EcsOpOpaqueStruct:
case EcsOpOpaqueArray:
case EcsOpOpaqueVector:
case EcsOpOpaqueValue:
break;

// Forward to type. Used for members of array/vector types.
case EcsOpForward:
serialize(world, op->type, ptr);
break;

// Serialize single values
case EcsOpEnum:
serializeEnum(op, ptr);
break;
case EcsOpBitmask:
// Bitmask serialization requires iterating all the bits in a value
// and looking up the corresponding constant. For an example, see
// src/addons/script/serialize.c.
break;
case EcsOpBool:
printf("%s", (*(const bool*)ptr) ? "true" : "false");
break;
case EcsOpChar:
printf("'%c'", *(const char*)ptr);
break;
case EcsOpByte:
case EcsOpU8:
printf("%" PRIu8, *(const uint8_t*)ptr);
break;
case EcsOpU16:
printf("%" PRIu16, *(const uint16_t*)ptr);
break;
case EcsOpU32:
printf("%" PRIu32, *(const uint32_t*)ptr);
break;
case EcsOpU64:
printf("%" PRIu64, *(const uint64_t*)ptr);
break;
case EcsOpI8:
printf("%" PRIi8, *(const int8_t*)ptr);
break;
case EcsOpI16:
printf("%" PRIi16, *(const int16_t*)ptr);
break;
case EcsOpI32:
printf("%" PRIi32, *(const int32_t*)ptr);
break;
case EcsOpI64:
printf("%" PRIi64, *(const int64_t*)ptr);
break;
case EcsOpF32:
printf("%.2f", (double)*(const float*)ptr);
break;
case EcsOpF64:
printf("%.2f", *(const double*)ptr);
break;
case EcsOpUPtr:
printf("%" PRIuPTR, *(const uintptr_t*)ptr);
break;
case EcsOpIPtr:
printf("%" PRIiPTR, *(const intptr_t*)ptr);
break;
case EcsOpString:
printf("\"%s\"", *(const char**)ptr);
break;
case EcsOpEntity: {
char *name = ecs_get_path(world, *(ecs_entity_t*)ptr);
printf("%s\n", name);
ecs_os_free(name);
break;
}
case EcsOpId: {
char *name = ecs_id_str(world, *(ecs_id_t*)ptr);
printf("%s\n", name);
ecs_os_free(name);
break;
}
case EcsOpPop:
case EcsOpScope:
case EcsOpPrimitive:
// Not serializable
break;
}

i += op->op_count - 1; // Skip over already processed instructions
}
}

// Some types to test the serializer with

typedef struct {
uint8_t red;
uint8_t green;
uint8_t blue;
} Rgb;

typedef enum {
Solid, Dashed
} StrokeKind;

typedef struct {
float x, y;
} Point;

typedef struct {
Rgb color;
Rgb stroke_color;
StrokeKind stroke_kind;
ecs_vec_t points;
} Polygon;

int main(int argc, char *argv[]) {
ecs_world_t *world = ecs_init_w_args(argc, argv);

// We need to register reflection data for our types before we can use them
// with the serializer code. We'll do it manually here, but for an easier
// way to insert reflection data see the auto_* examples.

ECS_COMPONENT(world, Rgb);
ECS_COMPONENT(world, StrokeKind);
ECS_COMPONENT(world, Point);
ECS_COMPONENT(world, Polygon);

ecs_struct(world, {
.entity = ecs_id(Rgb),
.members = {
{ .name = "r", .type = ecs_id(ecs_u8_t) },
{ .name = "g", .type = ecs_id(ecs_u8_t) },
{ .name = "b", .type = ecs_id(ecs_u8_t) },
}
});

ecs_enum(world, {
.entity = ecs_id(StrokeKind),
.constants = {
{ .name = "Solid", .value = Solid },
{ .name = "Dashed", .value = Dashed },
}
});

ecs_struct(world, {
.entity = ecs_id(Point),
.members = {
{ .name = "x", .type = ecs_id(ecs_f32_t) },
{ .name = "y", .type = ecs_id(ecs_f32_t) }
}
});

ecs_entity_t PointVector = ecs_vector(world, { .type = ecs_id(Point) });

ecs_struct(world, {
.entity = ecs_id(Polygon),
.members = {
{ .name = "color", .type = ecs_id(Rgb) },
{ .name = "stroke_color", .type = ecs_id(Rgb) },
{ .name = "stroke_kind", .type = ecs_id(StrokeKind) },
{ .name = "points", .type = PointVector }
}
});

// Create the value
ecs_vec_t points;
ecs_vec_init_t(NULL, &points, Point, 0);
*ecs_vec_append_t(NULL, &points, Point) = (Point){0, 0};
*ecs_vec_append_t(NULL, &points, Point) = (Point){1, 1};
*ecs_vec_append_t(NULL, &points, Point) = (Point){-1, 1};

Polygon p = {
.color = {0, 0, 255},
.stroke_color = {255, 0, 0},
.stroke_kind = Dashed,
.points = points
};

// Serialize value
serialize(world, ecs_id(Polygon), &p);
printf("\n");

// Free memory
ecs_vec_fini_t(NULL, &points, Point);

ecs_fini(world);

// Output:
// {
// color: {
// r: 0,
// g: 0,
// b: 255
// },
// stroke_color: {
// r: 255,
// g: 0,
// b: 0
// },
// stroke_kind: Dashed,
// points: [{
// x: 0.00,
// y: 0.00
// }, {
// x: 1.00,
// y: 1.00
// }, {
// x: -1.00,
// y: 1.00
// }]
// }
}
5 changes: 5 additions & 0 deletions examples/cpp/reflection/custom_serializer/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.bake_cache
.DS_Store
.vscode
gcov
bin
Loading
Loading