Add helper macros for defining ecs components and their to_string() methods
All checks were successful
Run tests / Run tests (push) Successful in 7m21s
All checks were successful
Run tests / Run tests (push) Successful in 7m21s
This commit is contained in:
parent
75df1be94d
commit
4aa6760f64
2 changed files with 134 additions and 0 deletions
98
libs/ecs/include/psemek/ecs/define_component.hpp
Normal file
98
libs/ecs/include/psemek/ecs/define_component.hpp
Normal 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)) \
|
||||
};
|
||||
36
libs/ecs/include/psemek/ecs/to_string.hpp
Normal file
36
libs/ecs/include/psemek/ecs/to_string.hpp
Normal 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__); }
|
||||
Loading…
Add table
Reference in a new issue