Add helper macros for defining ecs components and their to_string() methods
All checks were successful
Run tests / Run tests (push) Successful in 7m21s

This commit is contained in:
Nikita Lisitsa 2026-08-14 16:48:06 +03:00
parent 75df1be94d
commit 4aa6760f64
2 changed files with 134 additions and 0 deletions

View file

@ -0,0 +1,98 @@
#pragma once
#include <psemek/ecs/declare_uuid.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/tuple/size.hpp>
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/variadic_seq_to_seq.hpp>
#include <format>
namespace psemek::ecs::detail
{
template <typename T>
struct define_component_type_helper;
template <typename T>
struct define_component_type_helper<void(T)>
{
using type = T;
};
}
#define psemek_ecs_define_component_detail_field_impl_2(value) \
typename ::psemek::ecs::detail::define_component_type_helper<void(BOOST_PP_TUPLE_ELEM(1, value))>::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<void(BOOST_PP_TUPLE_ELEM(1, value))>::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<float, 2>), the whole type must be put in parentheses, like
*
* psemek_ecs_define_component_fields(
* (position, (math::vector<float, 2>))
* )
*
* 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<float, 2>), (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)) \
};

View file

@ -0,0 +1,36 @@
#pragma once
#include <boost/preprocessor/control/if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <format>
#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__); }