Skip to content

Commit

Permalink
#124 add dynamic buffer examples
Browse files Browse the repository at this point in the history
  • Loading branch information
SanderMertens committed Jan 18, 2021
1 parent f46b4e8 commit 9560ed5
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 1 deletion.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,9 @@ The framework code and example code is compiled warning free on all platforms wi
Performance is tracked on a per-release basis, with the results for the latest release published here: https://github.com/SanderMertens/ecs_benchmark

### API stability
API stability is guaranteed between minor releases, except in the rare case when an API is found to be an obvious source of confusion or bugs. ABI stability is not guaranteed inbetween versions. Types and function signatures may change as long as they do not require changes in the application code, which is why applications should rebuild after upgrading to a new revision. Headers under include/private are not part of the public API, and may introduce breaking changes at any point.
API stability is guaranteed between minor releases, except in the rare case when an API is found to be an obvious source of confusion or bugs. ABI stability is not guaranteed inbetween versions. When breaking changes do happen, the release notes will mention it with potential workarounds.

Types and function signatures may change as long as they do not require changes in the application code, which is why applications should rebuild after upgrading to a new revision. Headers under include/private are not part of the public API, and may introduce breaking changes at any point.

It is generally safe to use the master branch, which contains the latest version of the code. New features that are on master but are not yet part of a release may still see changes in their API. Once a feature is part of a release, its API will not change until at least the next major release.

Expand Down
16 changes: 16 additions & 0 deletions examples/c/53_dynamic_buffer/include/dynamic_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef DYNAMIC_BUFFER_H
#define DYNAMIC_BUFFER_H

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

#ifdef __cplusplus
extern "C" {
#endif

#ifdef __cplusplus
}
#endif

#endif

24 changes: 24 additions & 0 deletions examples/c/53_dynamic_buffer/include/dynamic_buffer/bake_config.h
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 DYNAMIC_BUFFER_BAKE_CONFIG_H
#define DYNAMIC_BUFFER_BAKE_CONFIG_H

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

#endif

12 changes: 12 additions & 0 deletions examples/c/53_dynamic_buffer/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": "dynamic_buffer",
"type": "application",
"value": {
"author": "Jane Doe",
"description": "A simple hello world flecs application",
"public": false,
"use": [
"flecs"
]
}
}
131 changes: 131 additions & 0 deletions examples/c/53_dynamic_buffer/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#include <dynamic_buffer.h>

typedef struct {
float x, y;
} Position;

/* Non-POD component type with a dynamic buffer */
typedef struct {
int *data;
size_t count;
} DynamicBuffer;

/* Lifecycle callbacks for the DynamicBuffer. These ensure that when a component
* is created, destructed, copied or moved no memory corruption or leakage
* happens. */
ECS_CTOR(DynamicBuffer, ptr, {
printf("DynamicBuffer::ctor\n");
ptr->data = NULL;
ptr->count = 0;
})

ECS_DTOR(DynamicBuffer, ptr, {
printf("DynamicBuffer::dtor\n");
free(ptr->data);
})

ECS_COPY(DynamicBuffer, dst, src, {
printf("DynamicBuffer::copy\n");
if (dst->data) {
free(dst->data);
}

size_t size = sizeof(int) * src->count;
dst->data = malloc(size);
dst->count = src->count;
memcpy(dst->data, src->data, size);
})

ECS_MOVE(DynamicBuffer, dst, src, {
printf("DynamicBuffer::move\n");
if (dst->data) {
free(dst->data);
}

dst->data = src->data;
dst->count = src->count;

src->data = NULL;
src->count = 0;
})

/* Forward declare component handles */
ECS_COMPONENT_DECLARE(Position);
ECS_COMPONENT_DECLARE(DynamicBuffer);

/* Add an element to a new or existing buffer */
void add_elem(ecs_world_t *ecs, ecs_entity_t e, int value) {
DynamicBuffer *ptr = ecs_get_mut(ecs, e, DynamicBuffer, NULL);

ptr->count ++;
ptr->data = realloc(ptr->data, ptr->count * sizeof(int));
ptr->data[ptr->count - 1] = value;
}

/* Remove element from buffer */
void remove_elem(ecs_world_t *ecs, ecs_entity_t e, size_t elem) {
DynamicBuffer *ptr = ecs_get_mut(ecs, e, DynamicBuffer, NULL);

size_t last = ptr->count - 1;

if (last >= elem) {
if (last - elem) {
ptr->data[elem] = ptr->data[last];
}

ptr->count --;
}
}

/* Get element from a buffer */
int* get_elem(ecs_world_t *ecs, ecs_entity_t e, size_t elem) {
const DynamicBuffer *ptr = ecs_get(ecs, e, DynamicBuffer);

if (ptr && (ptr->count > elem)) {
return &ptr->data[elem];
} else {
return NULL;
}
}

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

/* Register ids for forward declared components */
ECS_COMPONENT_DEFINE(ecs, Position);
ECS_COMPONENT_DEFINE(ecs, DynamicBuffer);

/* Register lifecycle functions for comopnent */
ecs_set_component_actions(ecs, DynamicBuffer, {
ecs_ctor(DynamicBuffer),
ecs_dtor(DynamicBuffer),
ecs_copy(DynamicBuffer),
ecs_move(DynamicBuffer),
NULL /* optional context */
});

ecs_entity_t e = ecs_new_id(ecs);

/* Add 3 elements to the buffer. The first add will add the DynamicBuffer
* element to the entity. */
add_elem(ecs, e, 10);
add_elem(ecs, e, 20);
add_elem(ecs, e, 30);

printf("Elem 1 = %d\n", *get_elem(ecs, e, 1));

/* Remove element. This will move the last element from the buffer to the
* removed element. */
remove_elem(ecs, e, 1);

printf("Elem 1 = %d (after remove)\n", *get_elem(ecs, e, 1));

/* Add component. This causes the entity to move between tables, and will
* invoke DynamicComponent::move to copy the component value from the src to
* the dst table. This also invokes DynamicComponent::ctor to construct the
* component in the dst table. */
ecs_add(ecs, e, Position);

/* This will invoke DynamicComponent::dtor. */
return ecs_fini(ecs);
}
16 changes: 16 additions & 0 deletions examples/cpp/53_dynamic_buffer/include/dynamic_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef DYNAMIC_BUFFER_H
#define DYNAMIC_BUFFER_H

/* This generated file contains includes for project dependencies */
#include "dynamic_buffer/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 DYNAMIC_BUFFER_BAKE_CONFIG_H
#define DYNAMIC_BUFFER_BAKE_CONFIG_H

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

#endif

13 changes: 13 additions & 0 deletions examples/cpp/53_dynamic_buffer/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"id": "dynamic_buffer",
"type": "application",
"value": {
"author": "Jane Doe",
"description": "A simple hello world flecs application",
"public": false,
"use": [
"flecs"
],
"language": "c++"
}
}
114 changes: 114 additions & 0 deletions examples/cpp/53_dynamic_buffer/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#include <dynamic_buffer.h>
#include <iostream>

struct Position {
float x, y;
};

/* Non-POD type. Don't use std::vector on purpose to demonstrate how a type
* can register its own lifecycle actions. */
struct DynamicBuffer {
DynamicBuffer() : data(nullptr), count(0) {
std::cout << "DynamicBuffer::ctor" << std::endl;
}

~DynamicBuffer() {
std::cout << "DynamicBuffer::dtor" << std::endl;
free(data);
}

DynamicBuffer& operator=(const DynamicBuffer& src) {
std::cout << "DynamicBuffer::copy" << std::endl;
if (data) {
free(data);
}

size_t size = sizeof(int) * src.count;
data = static_cast<int*>(malloc(size));
count = src.count;
memcpy(data, src.data, size);

return *this;
}

DynamicBuffer& operator=(DynamicBuffer&& src) {
std::cout << "DynamicBuffer::move" << std::endl;
if (data) {
free(data);
}

data = src.data;
count = src.count;

src.data = nullptr;
src.count = 0;

return *this;
}

int *data;
size_t count;
};

/* Add an element to a new or existing buffer */
void add_elem(flecs::entity e, int value) {
DynamicBuffer *ptr = e.get_mut<DynamicBuffer>();

ptr->count ++;
ptr->data = static_cast<int*>(realloc(ptr->data, ptr->count * sizeof(int)));
ptr->data[ptr->count - 1] = value;
}

/* Remove element from buffer */
void remove_elem(flecs::entity e, size_t elem) {
DynamicBuffer *ptr = e.get_mut<DynamicBuffer>();

size_t last = ptr->count - 1;

if (last >= elem) {
if (last - elem) {
ptr->data[elem] = ptr->data[last];
}

ptr->count --;
}
}

/* Get element from a buffer */
int* get_elem(flecs::entity e, size_t elem) {
const DynamicBuffer *ptr = e.get<DynamicBuffer>();

if (ptr && (ptr->count > elem)) {
return &ptr->data[elem];
} else {
return NULL;
}
}

int main(int argc, char *argv[]) {
flecs::world ecs(argc, argv);

auto e = ecs.entity();

/* Add 3 elements to the buffer. The first add will add the DynamicBuffer
* element to the entity. */
add_elem(e, 10);
add_elem(e, 20);
add_elem(e, 30);

std::cout << "Elem 1 = " << *get_elem(e, 1) << std::endl;

/* Remove element. This will move the last element from the buffer to the
* removed element. */
remove_elem(e, 1);

std::cout << "Elem 1 = " << *get_elem(e, 1) << " (after remove)" << std::endl;

/* Add component. This causes the entity to move between tables, and will
* invoke DynamicComponent::move to copy the component value from the src to
* the dst table. This also invokes DynamicComponent::ctor to construct the
* component in the dst table. */
e.add<Position>();

/* World gets cleaned up, which invokes DynamicComponent::dtor. */
}

0 comments on commit 9560ed5

Please sign in to comment.