diff --git a/libs/ecs/include/psemek/ecs/define_component.hpp b/libs/ecs/include/psemek/ecs/define_component.hpp new file mode 100644 index 00000000..fad1003a --- /dev/null +++ b/libs/ecs/include/psemek/ecs/define_component.hpp @@ -0,0 +1,98 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace psemek::ecs::detail +{ + + template + struct define_component_type_helper; + + template + struct define_component_type_helper + { + using type = T; + }; + +} + +#define psemek_ecs_define_component_detail_field_impl_2(value) \ + typename ::psemek::ecs::detail::define_component_type_helper::type BOOST_PP_TUPLE_ELEM(0, value); + +#define psemek_ecs_define_component_detail_field_impl_3(value) \ + typename ::psemek::ecs::detail::define_component_type_helper::type BOOST_PP_TUPLE_ELEM(0, value) = BOOST_PP_TUPLE_ELEM(2, value); + +#define psemek_ecs_define_component_detail_field_impl(value, size) \ + BOOST_PP_CAT(psemek_ecs_define_component_detail_field_impl_, size)(value) + +#define psemek_ecs_define_component_detail_field(r, data, value) \ + psemek_ecs_define_component_detail_field_impl(value, BOOST_PP_TUPLE_SIZE(value)) + +#define psemek_ecs_define_component_detail_to_string_impl(fname) \ + result += std::format(BOOST_PP_STRINGIZE(fname) ": {}\n", fname); + +#define psemek_ecs_define_component_detail_to_string(r, data, value) \ + psemek_ecs_define_component_detail_to_string_impl(BOOST_PP_TUPLE_ELEM(0, value)) + +#define psemek_ecs_define_component_fields_impl(fields) \ + BOOST_PP_SEQ_FOR_EACH(psemek_ecs_define_component_detail_field, _, fields) \ + std::string to_string() const { \ + std::string result; \ + BOOST_PP_SEQ_FOR_EACH(psemek_ecs_define_component_detail_to_string, _, fields) \ + return result; \ + } + +/* Given a sequence of pairs or triples of the form + * + * (name, type) + * (name, type, default_value) + * + * emit a field with the specified type, name, and default_value (if present), + * and generate a to_string() const method that outputs each field on its own line + * using std::format. + * + * For example, + * + * psemek_ecs_define_component_fields( + * (position, float) + * (count, int, 0) + * ) + * + * emits + * + * float position; + * int count = 0; + * + * and the corresponding to_string() implementation. + * + * If the type contains commas (e.g. if it is a template specialization with several + * parameters, like math::vector), the whole type must be put in parentheses, like + * + * psemek_ecs_define_component_fields( + * (position, (math::vector)) + * ) + * + * If the default value contains commas, the same applies. If the default value is an initializer + * list, it (quite regrettably) must be converted to an explicit value within parentheses, e.g. + * + * psemek_ecs_define_component_fields( + * (position, (math::vector), (math::vector{100.f, 100.f})) + * ) + */ +#define psemek_ecs_define_component_fields(fields) \ + psemek_ecs_define_component_fields_impl(BOOST_PP_VARIADIC_SEQ_TO_SEQ(fields)) + +#define psemek_ecs_define_component(cname, fields) \ + struct cname { \ + psemek_ecs_define_component_fields_impl(BOOST_PP_VARIADIC_SEQ_TO_SEQ(fields)) \ + psemek_ecs_declare_uuid(BOOST_PP_STRINGIZE(cname)) \ + }; diff --git a/libs/ecs/include/psemek/ecs/to_string.hpp b/libs/ecs/include/psemek/ecs/to_string.hpp new file mode 100644 index 00000000..c1b9ece1 --- /dev/null +++ b/libs/ecs/include/psemek/ecs/to_string.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include + +#include + +#define psemek_ecs_to_string_detail_apply_one(r, data, i, elem) \ + BOOST_PP_IF(i, "\n", "") BOOST_PP_STRINGIZE(elem) ": {}" + +/* Format a sequence of values into lines of the form "name: value". + * For example, having + * + * int a = 10; + * float x = 3.5f; + * + * The call + * + * psemek_ecs_to_string(a, b) + * + * returns a string + * + * "a = 10\nx = 3.5\n" + */ +#define psemek_ecs_to_string(...) \ + std::format( \ + BOOST_PP_SEQ_FOR_EACH_I( \ + psemek_ecs_to_string_detail_apply_one, _, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) \ + ), \ + __VA_ARGS__ \ + ) + +#define psemek_ecs_declare_to_string(...) \ + std::string to_string() const { return psemek_ecs_to_string(__VA_ARGS__); }