Skip to content

Commit

Permalink
feat(memory): add AnyValue
Browse files Browse the repository at this point in the history
  • Loading branch information
RiscadoA committed Oct 11, 2023
1 parent ef72403 commit 067b6d6
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ set(CUBOS_CORE_SOURCE
"src/cubos/core/memory/stream.cpp"
"src/cubos/core/memory/standard_stream.cpp"
"src/cubos/core/memory/buffer_stream.cpp"
"src/cubos/core/memory/any_value.cpp"
"src/cubos/core/memory/any_vector.cpp"

"src/cubos/core/reflection/type.cpp"
Expand Down
67 changes: 67 additions & 0 deletions core/include/cubos/core/memory/any_value.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/// @file
/// @brief Class @ref cubos::core::memory::AnyValue.
/// @ingroup core-memory

#pragma once

namespace cubos::core::reflection
{
class Type;
} // namespace cubos::core::reflection

namespace cubos::core::memory
{
/// @brief Stores a blob of a given reflected type.
/// @ingroup core-memory
class AnyValue final
{
public:
~AnyValue();

/// @brief Move constructs.
/// @param other Value.
AnyValue(AnyValue&& other) noexcept;

/// @brief Default constructs a value with the given type.
/// @note @p type must have @ref reflection::ConstructibleTrait and be default
/// constructible.
/// @param type Value type.
/// @return Value.
static AnyValue defaultConstruct(const reflection::Type& type) noexcept;

/// @brief Copy constructs a value with the given type.
/// @note @p type must have @ref reflection::ConstructibleTrait and be copy constructible.
/// @param type Value type.
/// @param value Value to copy.
/// @return Value.
static AnyValue copyConstruct(const reflection::Type& type, const void* value) noexcept;

/// @brief Move constructs a value with the given type.
/// @note @p type must have @ref reflection::ConstructibleTrait and be move constructible.
/// @param type Value type.
/// @param value Value to move.
/// @return Value.
static AnyValue moveConstruct(const reflection::Type& type, void* value) noexcept;

/// @brief Get the type of the elements stored in the vector.
/// @return Element type.
const reflection::Type& type() const;

/// @brief Gets a pointer to the underlying value.
/// @return Value.
void* get();

/// @copydoc get()
const void* get() const;

private:
/// @brief Constructs with allocated uninitialized memory.
/// @warning The constructor must be called immediately after this. Will cause UB if the
/// returned value is deconstructed before being initialized.
/// @param type Value type.
AnyValue(const reflection::Type& type) noexcept;

const reflection::Type& mType;
void* mValue;
};
} // namespace cubos::core::memory
86 changes: 86 additions & 0 deletions core/src/cubos/core/memory/any_value.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#include <new>

#include <cubos/core/log.hpp>
#include <cubos/core/memory/any_value.hpp>
#include <cubos/core/memory/move.hpp>
#include <cubos/core/reflection/traits/constructible.hpp>
#include <cubos/core/reflection/type.hpp>

using cubos::core::memory::AnyValue;
using cubos::core::reflection::ConstructibleTrait;
using cubos::core::reflection::Type;

AnyValue::~AnyValue()
{
// Might have been moved, we must check it here.
if (mValue != nullptr)
{
const auto& trait = mType.get<ConstructibleTrait>();
trait.destruct(mValue);
operator delete(mValue, static_cast<std::align_val_t>(trait.alignment()));
}
}

AnyValue::AnyValue(AnyValue&& other) noexcept
: mType(other.mType)
, mValue(other.mValue)
{
other.mValue = nullptr;
}

AnyValue AnyValue::defaultConstruct(const Type& type) noexcept
{
CUBOS_ASSERT(type.has<ConstructibleTrait>(), "Type must be constructible");
const auto& trait = type.get<ConstructibleTrait>();
CUBOS_ASSERT(trait.hasDefaultConstruct(), "Type must be default constructible");

AnyValue any{type};
trait.defaultConstruct(any.get());
return move(any);
}

AnyValue AnyValue::copyConstruct(const Type& type, const void* value) noexcept
{
CUBOS_ASSERT(type.has<ConstructibleTrait>(), "Type must be constructible");
const auto& trait = type.get<ConstructibleTrait>();
CUBOS_ASSERT(trait.hasCopyConstruct(), "Type must be copy constructible");

AnyValue any{type};
trait.copyConstruct(any.get(), value);
return move(any);
}

AnyValue AnyValue::moveConstruct(const Type& type, void* value) noexcept
{
CUBOS_ASSERT(type.has<ConstructibleTrait>(), "Type must be constructible");
const auto& trait = type.get<ConstructibleTrait>();
CUBOS_ASSERT(trait.hasMoveConstruct(), "Type must be move constructible");

AnyValue any{type};
trait.moveConstruct(any.get(), value);
return move(any);
}

const Type& AnyValue::type() const
{
return mType;
}

void* AnyValue::get()
{
return mValue;
}

const void* AnyValue::get() const
{
return mValue;
}

AnyValue::AnyValue(const Type& type) noexcept
: mType(type)
{
const auto& trait = type.get<ConstructibleTrait>();
mValue = operator new(trait.size(), static_cast<std::align_val_t>(trait.alignment()), std::nothrow);

CUBOS_ASSERT(mValue != nullptr, "Could not allocate memory for value of type '{}'", type.name());
}
1 change: 1 addition & 0 deletions core/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ add_executable(
data/fs/file_system.cpp
data/context.cpp

memory/any_value.cpp
memory/any_vector.cpp
memory/type_map.cpp
memory/unordered_bimap.cpp
Expand Down
52 changes: 52 additions & 0 deletions core/tests/memory/any_value.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include <doctest/doctest.h>

#include <cubos/core/memory/any_value.hpp>
#include <cubos/core/reflection/external/primitives.hpp>
#include <cubos/core/reflection/type.hpp>

#include "../utils.hpp"

TEST_CASE("memory::AnyValue")
{
using cubos::core::memory::AnyValue;
using cubos::core::reflection::reflect;

SUBCASE("with integers")
{
SUBCASE("defaultConstruct")
{
const auto any = AnyValue::defaultConstruct(reflect<int>());
CHECK(any.type().is<int>());
CHECK(*static_cast<const int*>(any.get()) == 0);
}

SUBCASE("copyConstruct")
{
int value = 1337;
auto any = AnyValue::copyConstruct(reflect<int>(), &value);
CHECK(any.type().is<int>());
CHECK(*static_cast<int*>(any.get()) == value);
}

SUBCASE("moveConstruct")
{
int value = 1337;
auto any = AnyValue::moveConstruct(reflect<int>(), &value);
CHECK(any.type().is<int>());
CHECK(*static_cast<int*>(any.get()) == value);
}
}

SUBCASE("detect destructor")
{
bool destructed = false;
DetectDestructor detector{&destructed};

{
auto any = AnyValue::moveConstruct(reflect<DetectDestructor>(), &detector);
CHECK_FALSE(destructed);
}

CHECK(destructed);
}
}

0 comments on commit 067b6d6

Please sign in to comment.