Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define reflection for primitive types #543

Merged
merged 5 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ set(CUBOS_CORE_SOURCE

"src/cubos/core/reflection/type.cpp"
"src/cubos/core/reflection/traits/constructible.cpp"
"src/cubos/core/reflection/external/primitives.cpp"

"src/cubos/core/data/serializer.cpp"
"src/cubos/core/data/deserializer.cpp"
Expand Down Expand Up @@ -102,6 +103,7 @@ set(CUBOS_CORE_INCLUDE
"include/cubos/core/reflection/reflect.hpp"
"include/cubos/core/reflection/type.hpp"
"include/cubos/core/reflection/traits/constructible.hpp"
"include/cubos/core/reflection/external/primitives.hpp"

"include/cubos/core/data/serializer.hpp"
"include/cubos/core/data/deserializer.hpp"
Expand Down
2 changes: 2 additions & 0 deletions core/include/cubos/core/reflection/external/module.dox
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/// @dir
/// @brief @ref Reflection declarations for external types.
22 changes: 22 additions & 0 deletions core/include/cubos/core/reflection/external/primitives.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// @file
/// @brief Reflection declarations for primitive types.
/// @ingroup core-reflection

#pragma once

#include <cstdint>

#include <cubos/core/reflection/reflect.hpp>

CUBOS_REFLECT_EXTERNAL_DECL(bool);
CUBOS_REFLECT_EXTERNAL_DECL(char);
CUBOS_REFLECT_EXTERNAL_DECL(int8_t);
CUBOS_REFLECT_EXTERNAL_DECL(int16_t);
CUBOS_REFLECT_EXTERNAL_DECL(int32_t);
CUBOS_REFLECT_EXTERNAL_DECL(int64_t);
CUBOS_REFLECT_EXTERNAL_DECL(uint8_t);
CUBOS_REFLECT_EXTERNAL_DECL(uint16_t);
CUBOS_REFLECT_EXTERNAL_DECL(uint32_t);
CUBOS_REFLECT_EXTERNAL_DECL(uint64_t);
CUBOS_REFLECT_EXTERNAL_DECL(float);
CUBOS_REFLECT_EXTERNAL_DECL(double);
26 changes: 26 additions & 0 deletions core/src/cubos/core/reflection/external/primitives.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <cubos/core/reflection/external/primitives.hpp>
#include <cubos/core/reflection/traits/constructible.hpp>
#include <cubos/core/reflection/type.hpp>

#define AUTO_IMPL(type, name) \
CUBOS_REFLECT_EXTERNAL_IMPL(type) \
{ \
return Type::create(name).with(ConstructibleTrait::typed<type>() \
.withDefaultConstructor() \
.withCopyConstructor() \
.withMoveConstructor() \
.build()); \
}

AUTO_IMPL(bool, "bool")
AUTO_IMPL(char, "char")
AUTO_IMPL(int8_t, "int8_t")
AUTO_IMPL(int16_t, "int16_t")
AUTO_IMPL(int32_t, "int32_t")
AUTO_IMPL(int64_t, "int64_t")
AUTO_IMPL(uint8_t, "uint8_t")
AUTO_IMPL(uint16_t, "uint16_t")
AUTO_IMPL(uint32_t, "uint32_t")
AUTO_IMPL(uint64_t, "uint64_t")
AUTO_IMPL(float, "float")
AUTO_IMPL(double, "double")
1 change: 1 addition & 0 deletions core/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_executable(
reflection/reflect.cpp
reflection/type.cpp
reflection/traits/constructible.cpp
reflection/external/primitives.cpp

data/fs/embedded_archive.cpp
data/fs/standard_archive.cpp
Expand Down
29 changes: 29 additions & 0 deletions core/tests/reflection/external/primitives.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <doctest/doctest.h>

#include <cubos/core/reflection/external/primitives.hpp>
#include <cubos/core/reflection/traits/constructible.hpp>

#include "../traits/constructible.hpp"

template <typename T>
static void test(const char* name, T value)
{
CHECK(reflect<T>().name() == name);
testConstructible<T>(&value);
}

TEST_CASE("reflection::reflect<(primitives)>()")
{
test<bool>("bool", true);
test<char>("char", 'A');
test<int8_t>("int8_t", INT8_MAX);
test<int16_t>("int16_t", INT16_MIN);
test<int32_t>("int32_t", INT32_MAX);
test<int64_t>("int64_t", INT64_MIN);
test<uint8_t>("uint8_t", UINT8_MAX);
test<uint16_t>("uint16_t", UINT16_MAX);
test<uint32_t>("uint32_t", UINT32_MAX);
test<uint64_t>("uint64_t", UINT64_MAX);
test<float>("float", 3.14159F);
test<double>("double", 0.123456789);
}
113 changes: 113 additions & 0 deletions core/tests/reflection/traits/constructible.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#pragma once

#include <concepts>

#include <cubos/core/reflection/external/primitives.hpp>
#include <cubos/core/reflection/traits/constructible.hpp>
#include <cubos/core/reflection/type.hpp>

#include "../../utils.hpp"

using cubos::core::reflection::ConstructibleTrait;
using cubos::core::reflection::reflect;
using cubos::core::reflection::Type;

/// @brief Checks if a type's ConstructibleTrait has the correct size and alignment.
/// @tparam T Type.
template <typename T>
static void testConstructibleLayout()
{
const Type& type = reflect<T>();
REQUIRE(type.has<ConstructibleTrait>());
auto& constructible = type.get<ConstructibleTrait>();

CHECK(constructible.size() == sizeof(T));
CHECK(constructible.alignment() == static_cast<std::size_t>(alignof(T)));
}

/// @brief Checks if a type's ConstructibleTrait default constructor works as expected.
/// @tparam T Type.
template <typename T>
static void testDefaultConstructible()
{
const Type& type = reflect<T>();
REQUIRE(type.has<ConstructibleTrait>());
auto& constructible = type.get<ConstructibleTrait>();

auto* instance = operator new(constructible.size());
REQUIRE(constructible.defaultConstruct(instance));

if constexpr (std::equality_comparable<T>)
{
CHECK(*reinterpret_cast<T*>(instance) == T{});
}

constructible.destruct(instance);
operator delete(instance);
}

/// @brief Checks if a type's ConstructibleTrait copy constructor works as expected.
/// @tparam T Type.
/// @param valueToCopy Value which will be copied.
template <typename T>
static void testCopyConstructible(const T& valueToCopy)
{
const Type& type = reflect<T>();
REQUIRE(type.has<ConstructibleTrait>());
auto& constructible = type.get<ConstructibleTrait>();

auto* instance = operator new(constructible.size());
REQUIRE(constructible.copyConstruct(instance, &valueToCopy));

if constexpr (std::equality_comparable<T>)
{
CHECK(*reinterpret_cast<T*>(instance) == valueToCopy);
}

constructible.destruct(instance);
operator delete(instance);
}

/// @brief Checks if a type's ConstructibleTrait move constructor works as expected.
/// @tparam T Type.
/// @param valueToMove Value which will be moved.
template <typename T>
static void testMoveConstructible(T& valueToMove)
{
const Type& type = reflect<T>();
REQUIRE(type.has<ConstructibleTrait>());
auto& constructible = type.get<ConstructibleTrait>();

auto* instance = operator new(constructible.size());
REQUIRE(constructible.moveConstruct(instance, &valueToMove));

constructible.destruct(instance);
operator delete(instance);
}

/// @brief Checks if a type's ConstructibleTrait has the expected information.
/// @tparam T Type.
/// @param valueToMove Value which will be moved - necessary if the type is copy or move
/// constructible.
template <typename T>
void testConstructible(T* valueToMove = nullptr)
{
testConstructibleLayout<T>();

if constexpr (std::default_initializable<T>)
{
testDefaultConstructible<T>();
}

if constexpr (std::copy_constructible<T>)
{
REQUIRE(valueToMove != nullptr);
testCopyConstructible<T>(*valueToMove);
}

if constexpr (std::move_constructible<T>)
{
REQUIRE(valueToMove != nullptr);
testMoveConstructible<T>(*valueToMove);
}
}