Compare commits
9 commits
1265b816d3
...
4aa6760f64
| Author | SHA1 | Date | |
|---|---|---|---|
| 4aa6760f64 | |||
| 75df1be94d | |||
| 17be2f3aaa | |||
| ce5531dbbb | |||
| 99063348e3 | |||
| abec5474f0 | |||
| d42639edba | |||
| c96766659c | |||
| 305d1556c2 |
12 changed files with 380 additions and 16 deletions
|
|
@ -3,7 +3,7 @@ project(psemek)
|
|||
|
||||
cmake_policy(SET CMP0077 NEW)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
string(TOUPPER "${CMAKE_BUILD_TYPE}" PSEMEK_BUILD_TYPE)
|
||||
|
|
|
|||
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)) \
|
||||
};
|
||||
|
|
@ -29,7 +29,10 @@ namespace psemek::ecs
|
|||
|
||||
inline std::ostream & operator << (std::ostream & out, handle const & handle)
|
||||
{
|
||||
out << '(' << handle.id << ',' << handle.epoch << ')';
|
||||
if (!handle)
|
||||
out << "(none)";
|
||||
else
|
||||
out << '(' << handle.id << ',' << handle.epoch << ')';
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
@ -55,9 +58,13 @@ namespace std
|
|||
return ctx.begin();
|
||||
}
|
||||
|
||||
auto format(::psemek::ecs::handle const & handle, std::format_context & ctx) const
|
||||
template <typename FormatContext>
|
||||
auto format(::psemek::ecs::handle const & handle, FormatContext & ctx) const
|
||||
{
|
||||
return std::format_to(ctx.out(), "({},{})", handle.id, handle.epoch);
|
||||
if (!handle)
|
||||
return std::format_to(ctx.out(), "(none)");
|
||||
else
|
||||
return std::format_to(ctx.out(), "({},{})", handle.id, handle.epoch);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
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__); }
|
||||
|
|
@ -4,6 +4,8 @@
|
|||
#include <psemek/math/rotation.hpp>
|
||||
#include <psemek/math/translation.hpp>
|
||||
|
||||
#include <format>
|
||||
|
||||
namespace psemek::math
|
||||
{
|
||||
|
||||
|
|
@ -86,3 +88,24 @@ namespace psemek::math
|
|||
};
|
||||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<::psemek::math::trs<T, 3>, Char>
|
||||
{
|
||||
template <typename FormatContext>
|
||||
constexpr auto parse(FormatContext & ctx) const
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(::psemek::math::trs<T, 3> const & trs, FormatContext & ctx) const
|
||||
{
|
||||
return std::format_to(ctx.out(), "translation: {}\nrotation: {}\nscale: {}", trs.translation, trs.rotation, trs.scale);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <format>
|
||||
|
||||
#include <psemek/random/device.hpp>
|
||||
|
||||
|
|
@ -66,6 +67,9 @@ namespace psemek::random
|
|||
read(stream, gen.inc_);
|
||||
}
|
||||
|
||||
template <typename T, typename Char>
|
||||
friend struct std::formatter;
|
||||
|
||||
private:
|
||||
std::uint64_t state_ = 0;
|
||||
std::uint64_t inc_ = 0;
|
||||
|
|
@ -78,3 +82,21 @@ namespace psemek::random
|
|||
};
|
||||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <typename Char>
|
||||
struct formatter<::psemek::random::generator, Char>
|
||||
: formatter<std::uint64_t>
|
||||
{
|
||||
using base = formatter<std::uint64_t>;
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(::psemek::random::generator const & rng, FormatContext & ctx) const
|
||||
{
|
||||
return std::format_to(ctx.out(), "({}, {})", rng.state_, rng.inc_);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <cstdint>
|
||||
#include <concepts>
|
||||
#include <limits>
|
||||
#include <format>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
|
@ -363,6 +364,25 @@ namespace psemek::util
|
|||
return FP::from_rep(((x.rep() - static_cast<rep_type>(1)) & ~fractional_mask) + fixed_point_one);
|
||||
}
|
||||
|
||||
template <unsigned int I, unsigned int F, bool S>
|
||||
constexpr fixed_point<I, F, S> trunc(fixed_point<I, F, S> x)
|
||||
{
|
||||
if (S && x < fixed_point<I, F, S>{0})
|
||||
{
|
||||
return ceil(x);
|
||||
}
|
||||
else
|
||||
{
|
||||
return floor(x);
|
||||
}
|
||||
}
|
||||
|
||||
template <unsigned int I, unsigned int F, bool S>
|
||||
constexpr fixed_point<I, F, S> fract(fixed_point<I, F, S> x)
|
||||
{
|
||||
return x - trunc(x);
|
||||
}
|
||||
|
||||
template <unsigned int I, unsigned int F, bool S>
|
||||
constexpr fixed_point<I, F, S> round(fixed_point<I, F, S> x)
|
||||
{
|
||||
|
|
@ -436,3 +456,21 @@ namespace psemek::util
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <unsigned int I, unsigned int F, bool S, typename Char>
|
||||
struct formatter<::psemek::util::fixed_point<I, F, S>, Char>
|
||||
: formatter<double>
|
||||
{
|
||||
using base = formatter<double>;
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(::psemek::util::fixed_point<I, F, S> const & fp, FormatContext & ctx) const
|
||||
{
|
||||
return base::format(static_cast<double>(fp), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
118
libs/util/include/psemek/util/format.hpp
Normal file
118
libs/util/include/psemek/util/format.hpp
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
#pragma once
|
||||
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <variant>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
concept has_format_impl = requires (T x, std::format_context & format_context)
|
||||
{
|
||||
format_impl(x, format_context);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <typename T, typename Char>
|
||||
requires ::psemek::util::has_format_impl<T>
|
||||
struct formatter<T, Char>
|
||||
{
|
||||
constexpr auto parse(std::format_parse_context & ctx) const
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(T const & x, FormatContext & ctx) const
|
||||
{
|
||||
return format_impl(x, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
requires (!std::is_same_v<T, void> && !std::is_same_v<T, Char>)
|
||||
struct formatter<T *, Char>
|
||||
: formatter<void *>
|
||||
{
|
||||
using base = formatter<void *, Char>;
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(T const * ptr, FormatContext & ctx) const
|
||||
{
|
||||
if (ptr)
|
||||
return base::format((void *)ptr, ctx);
|
||||
else
|
||||
return std::format_to(ctx.out(), "(null)");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<unique_ptr<T>, Char>
|
||||
: formatter<void *>
|
||||
{
|
||||
using base = formatter<void *, Char>;
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(unique_ptr<T> const & ptr, FormatContext & ctx) const
|
||||
{
|
||||
if (ptr)
|
||||
return base::format((void *)ptr.get(), ctx);
|
||||
else
|
||||
return std::format_to(ctx.out(), "(null)");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<shared_ptr<T>, Char>
|
||||
: formatter<void *>
|
||||
{
|
||||
using base = formatter<void *, Char>;
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(shared_ptr<T> const & ptr, FormatContext & ctx) const
|
||||
{
|
||||
if (ptr)
|
||||
return base::format((void *)ptr.get(), ctx);
|
||||
else
|
||||
return std::format_to(ctx.out(), "(null)");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<optional<T>, Char>
|
||||
: formatter<T>
|
||||
{
|
||||
using base = formatter<T, Char>;
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(optional<T> const & opt, FormatContext & ctx) const
|
||||
{
|
||||
if (opt)
|
||||
return base::format(*opt, ctx);
|
||||
else
|
||||
return std::format_to(ctx.out(), "(none)");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ... Args, typename Char>
|
||||
struct formatter<variant<Args...>, Char>
|
||||
{
|
||||
constexpr auto parse(std::format_parse_context & ctx) const
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(variant<Args...> const & v, FormatContext & ctx) const
|
||||
{
|
||||
return std::visit([&ctx](auto const & v){ return std::format_to(ctx.out(), "{}", v); }, v);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -23,8 +23,8 @@ namespace psemek::util
|
|||
struct function<R(Args...)>
|
||||
{
|
||||
private:
|
||||
static constexpr std::size_t storage_size = sizeof(void*) * 3;
|
||||
static constexpr std::size_t storage_align = alignof(void*);
|
||||
static constexpr std::size_t storage_size = sizeof(void *) * 3;
|
||||
static constexpr std::size_t storage_align = alignof(void *);
|
||||
|
||||
template <typename T>
|
||||
struct uses_static_storage
|
||||
|
|
@ -81,7 +81,7 @@ namespace psemek::util
|
|||
void swap(function & other) noexcept;
|
||||
|
||||
private:
|
||||
std::aligned_storage_t<storage_size, storage_align> storage_;
|
||||
alignas(storage_align) std::byte storage_[storage_size];
|
||||
|
||||
struct vtable
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
|
||||
#define psemek_declare_pimpl \
|
||||
|
|
@ -23,7 +22,7 @@ namespace psemek::util::pimpl
|
|||
template <typename Parent>
|
||||
struct impl;
|
||||
|
||||
template <typename Parent, std::size_t Size>
|
||||
template <typename Parent, std::size_t Size, std::size_t Align = alignof(std::max_align_t)>
|
||||
struct in_place
|
||||
{
|
||||
protected:
|
||||
|
|
@ -49,8 +48,7 @@ namespace psemek::util::pimpl
|
|||
}
|
||||
|
||||
private:
|
||||
|
||||
typename std::aligned_storage<Size>::type storage_;
|
||||
alignas(Align) std::byte storage_[Size];
|
||||
};
|
||||
|
||||
template <typename Parent>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <iostream>
|
||||
#include <bit>
|
||||
#include <format>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
|
@ -44,6 +46,8 @@ namespace psemek::util
|
|||
return uuid;
|
||||
}
|
||||
|
||||
// Make a version 3 UUID (name-based, using MD5)
|
||||
// NB: the namespace UUID is omitted (empty)
|
||||
// TODO: use SHA-1 or something like that?
|
||||
constexpr uuid make_uuid(std::string_view str)
|
||||
{
|
||||
|
|
@ -76,4 +80,21 @@ namespace std
|
|||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct formatter<::psemek::util::uuid, Char>
|
||||
{
|
||||
constexpr auto parse(std::format_parse_context & ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(::psemek::util::uuid const & uuid, FormatContext & ctx) const
|
||||
{
|
||||
auto x0 = std::byteswap(uuid[0]);
|
||||
auto x1 = std::byteswap(uuid[1]);
|
||||
return std::format_to(ctx.out(), "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}", x0 & 0xffffffffull, (x0 >> 4) & 0xfffful, (x0 >> 6) & 0xfffful, x1 & 0xfffful, (x1 >> 2) & 0xffffffffffffull);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,16 +7,19 @@ namespace psemek::util
|
|||
|
||||
std::ostream & operator << (std::ostream & os, uuid const & uuid)
|
||||
{
|
||||
auto x0 = std::byteswap(uuid[0]);
|
||||
auto x1 = std::byteswap(uuid[1]);
|
||||
|
||||
os << std::hex << std::setfill('0') << std::left;
|
||||
os << std::setw(8) << (uuid[0] & 0x00000000ffffffffull);
|
||||
os << std::setw(8) << (x0 & 0xffffffffull);
|
||||
os << std::setw(1) << '-';
|
||||
os << std::setw(4) << ((uuid[0] & 0x0000ffff00000000ull) >> 32);
|
||||
os << std::setw(4) << ((x0 >> 4) & 0xfffful);
|
||||
os << std::setw(1) << '-';
|
||||
os << std::setw(4) << ((uuid[0] & 0xffff000000000000ull) >> 48);
|
||||
os << std::setw(4) << ((x0 >> 6) & 0xfffful);
|
||||
os << std::setw(1) << '-';
|
||||
os << std::setw(4) << (uuid[1] & 0x000000000000ffffull);
|
||||
os << std::setw(4) << (x1 & 0xfffful);
|
||||
os << std::setw(1) << '-';
|
||||
os << std::setw(12) << ((uuid[1] & 0xffffffffffff0000ull) >> 16);
|
||||
os << std::setw(12) << ((x1 >> 2) & 0xffffffffffffull);
|
||||
|
||||
return os;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue