Skip to content

Commit

Permalink
variadic inheritance (yes, really)
Browse files Browse the repository at this point in the history
  • Loading branch information
jadebenn committed Jan 25, 2024
1 parent 1fc570f commit 6d1d7fc
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 82 deletions.
1 change: 1 addition & 0 deletions dCommon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set(DCOMMON_SOURCES
"Game.cpp"
"GeneralUtils.cpp"
"LDFFormat.cpp"
"MD5.cpp"
"Metrics.cpp"
"NiPoint3.cpp"
"NiQuaternion.cpp"
Expand Down
File renamed without changes.
File renamed without changes.
113 changes: 49 additions & 64 deletions dGame/dEntity/Archetype.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef __ARCHETYPE_H__
#define __ARCHETYPE_H__

#include <concepts>
#include <cstdint>
#include <type_traits>
#include <unordered_map>
Expand All @@ -10,129 +11,118 @@

// Require the template type to be of component base class
template <typename T>
concept ComponentType = std::is_base_of_v<Component, T>;
concept ComponentType = std::derived_from<T, Component>;

// Forward declaration of archetype class
template <ComponentType... CTypes>
class Archetype;

// Container struct for storing component data
template <typename T>
struct Container {
using type = T; // Alias to allow retrieval of entry type

using storage_type = std::vector<T>; //Alias for the archetype component container type
//using storage_type = std::array<T, 100>;

storage_type entries;
};

/**
* Base class to allow pointer/reference resolution
* Contains less-performant versions of methods to allow runtime resolution of component types
*/
class IArchetype {
class ArchetypeBase {
public:
/**
* Create an alias type for the Archetype ID
*/
using ArchetypeId = size_t;
ArchetypeId id{ 0 };

/**
* Create an alias for the archetype component container type
*/
template <typename T>
using ContainerType = std::vector<T>;

/**
* Check if a component type is contained by the archetype
* @returns Boolean value representing whether the component type is present in the archetype
*/
template <ComponentType CType>
[[nodiscard]] bool HasComponent() noexcept {
return m_ContainerPointers.contains(std::type_index(typeid(CType))) != 0;
return dynamic_cast<Container<CType>&>(*this).template HasComponent<CType>(); // TODO: Change this method!
}

/**
* Get component container by way of indirect pointer/reference
* @returns A reference to the component container
*/
template <ComponentType CType>
[[nodiscard]] ContainerType<CType>& Container() {
return *reinterpret_cast<ContainerType<CType>*>(m_ContainerPointers[std::type_index(typeid(CType))]);
[[nodiscard]] Container<CType>::storage_type& GetContainer() { // TODO: Try to avoid using this!
return dynamic_cast<Container<CType>&>(*this).template Container<CType>::entries;
}

virtual ~IArchetype() = default;
virtual ~ArchetypeBase() = default;

protected:
IArchetype(ArchetypeId id) noexcept : id{ id } {}
ArchetypeBase() = default;

/**
* Unordered map containing void pointers to each container
*/
std::unordered_map<std::type_index, void*> m_ContainerPointers;
constexpr explicit ArchetypeBase(ArchetypeId id) noexcept : id{ id } {};
};

/**
* The archetype class stores a variable number of entity component types TODO: EXPAND ON
*/
template <ComponentType... CTypes>
class Archetype final : public IArchetype {
class Archetype final : public ArchetypeBase, public Container<CTypes>... {
public:
constexpr explicit Archetype(ArchetypeId id) noexcept : IArchetype{ id } {
/**
* Alias that extracts the type of the Nth element passed as a template argument
*/
template <size_t N>
using type_index = std::tuple_element<N, std::tuple<CTypes...>>::type;

/**
* Constructor
*/
constexpr explicit Archetype(ArchetypeId id) noexcept : ArchetypeBase{ id } {
// Reserve 16 KB of memory for the sum of all vectors ahead of time
constexpr size_t compBytes = (sizeof(CTypes) + ...);
constexpr size_t reservedBytes = 16000;
constexpr size_t reserveNumEntries = reservedBytes / compBytes;
(Container<CTypes>().reserve(reserveNumEntries), ...);

// Create void pointers to each component container present
(m_ContainerPointers.emplace(typeid(CTypes), reinterpret_cast<void*>(&Container<CTypes>())), ...);
}

/**
* Constructors and assignment operators
*/
Archetype(Archetype& other) noexcept : IArchetype{ std::copy(other) } { /*UpdatePointers();*/ } // Copy constructor
Archetype(Archetype&& other) noexcept : IArchetype{ std::move(other) } { /*UpdatePointers();*/ } // Move constructor
Archetype& operator=(Archetype& other) noexcept { // Copy assignment operator
IArchetype::operator=(std::copy(other));
/*UpdatePointers();*/
}
Archetype& operator=(Archetype&& other) noexcept { // Move assignment operator
IArchetype::operator=(std::move(other));
/*UpdatePointers();*/
(Container<CTypes>::entries.reserve(reserveNumEntries), ...);
}

/**
* Get the size of the archetype (by taking the size of the first member container)
* @returns The size of the archetype's containers
*/
[[nodiscard]] constexpr size_t size() noexcept {
return std::get<0>(m_Components).size();
Container<type_index<0>>::entries.size();
return Container<type_index<0>>::entries.size();
}

/**
* Get if the container is empty (by only checking the first member container)
* @returns Boolean representing whether the container is empty
*/
[[nodiscard]] constexpr bool empty() noexcept {
return std::get<0>(m_Components).empty();
}

/**
* Get a reference to the component container of an archetype.
* @returns A reference to the archetype's container of components
*/
template <ComponentType CType>
[[nodiscard]] constexpr ContainerType<CType>& Container() noexcept {
static_assert(HasComponent<CType>(), "Archetype does not have container of requested component!"); // Compile-time verification
return std::get<ContainerType<CType>>(m_Components);
return Container<type_index<0>>::entries.empty();
}

/**
* Creates the archetype's components at the end of the container.
* @param componentArgs Arguments to be forwarded to the component constructors
*/
void CreateComponents(CTypes&&... componentArgs) noexcept {
(Container<CTypes>().emplace_back(std::forward<CTypes>(componentArgs)), ...);
constexpr void CreateComponents(CTypes&&... componentArgs) noexcept {
(Container<CTypes>::entries.emplace_back(std::forward<CTypes>(componentArgs)), ...);
}

/**
* Delete's the archetype's components at a specified container index, then moves the last element in the container to it.
* @param index The archetype container index to delete
*/
void DeleteComponents(const size_t index) {
constexpr void DeleteComponents(const size_t index) {
if (empty()) return; // Do not delete if the container is already empty

((Container<CTypes>().at(index) = std::move(Container<CTypes>().back()),
Container<CTypes>().pop_back()), ...);
((Container<CTypes>::entries.at(index) = std::move(Container<CTypes>::entries.back()),
Container<CTypes>::entries.pop_back()), ...);
}

/**
Expand All @@ -142,7 +132,7 @@ class Archetype final : public IArchetype {
*/
template <ComponentType CType>
[[nodiscard]] CType& GetComponent(const size_t index) {
return Container<CType>().at(index);
return Container<CType>::entries.at(index);
}

/**
Expand All @@ -157,20 +147,15 @@ class Archetype final : public IArchetype {
/**
* Contains the number of component types an archetype consists of
*/
static constexpr size_t numTypes = sizeof...(CTypes);
static constexpr size_t num_types = sizeof...(CTypes);

template<ComponentType CType>
struct contains : std::disjunction<std::is_same<CType, CTypes>...> {};

private:
/**
* Update void pointer locations in memory (to be called by move and copy constructors)
*/
constexpr void UpdatePointers() noexcept {
((m_ContainerPointers[std::type_index(typeid(CTypes))] = reinterpret_cast<void*>(Container<CTypes>())), ...);
}
template<ComponentType CType>
using contains_v = contains<CType>::value;

std::tuple<ContainerType<CTypes>...> m_Components; // Made it a tuple of vectors (may God help us all)
private:
//std::unordered_map<eReplicaComponentType, ArchetypeEdge<Types...>> edges;
};

Expand Down
20 changes: 10 additions & 10 deletions tests/dGameTests/dEntitiesTests/ArchetypeTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ TEST_F(ArchetypeTest, PlacementNewAddArchetypeTest) {

size_t destCompSize = sizeof(baseArchetype->GetComponent<DestroyableComponent>(1));
LOG("Destroyable component is of %ul size!", destCompSize);
auto nEntries = baseArchetype->Container<DestroyableComponent>().capacity();
auto nEntries = baseArchetype->Container<DestroyableComponent>::entries.capacity();
LOG("Archetype has %d entries!", nEntries);
}

Expand Down Expand Up @@ -131,7 +131,7 @@ TEST_F(ArchetypeTest, ArchetypeDeleteTest) {

/*TEST_F(ArchetypeTest, AddEntityTest) {
// Create the archetypes
std::array<std::unique_ptr<IArchetype>, 3> tempArchetype;
std::array<std::unique_ptr<ArchetypeBase>, 3> tempArchetype;
tempArchetype[0] = std::make_unique<Archetype<SimplePhysicsComponent>>(1);
tempArchetype[1] = std::make_unique<Archetype<DestroyableComponent, SimplePhysicsComponent>>(2);
tempArchetype[2] = std::make_unique<Archetype<DestroyableComponent>>(3);
Expand Down Expand Up @@ -221,17 +221,17 @@ TEST_F(ArchetypeTest, CreateArchetypesTest) {
ASSERT_EQ(gottenEntityId, baseEntityId);
}*/

TEST_F(ArchetypeTest, MoveIArchetypeTest) {
TEST_F(ArchetypeTest, MoveArchetypeBaseTest) {
// Insert an entry into the base archetype and set one trait for each
const auto baseEntityId = baseEntity->GetObjectID();
baseArchetype->CreateComponents(DestroyableComponent(baseEntityId), SimplePhysicsComponent(baseEntityId, 2));
baseArchetype->Container<DestroyableComponent>()[0].SetMaxHealth(30);
baseArchetype->Container<SimplePhysicsComponent>()[0].SetPosition(NiPoint3(1.0f, 2.0f, 3.0f));
baseArchetype->Container<DestroyableComponent>::entries[0].SetMaxHealth(30);
baseArchetype->Container<SimplePhysicsComponent>::entries[0].SetPosition(NiPoint3(1.0f, 2.0f, 3.0f));

// Move the archetype and compare the entry results, as well as checking the original is deleted
std::unique_ptr<IArchetype> movedArchetype = std::move(baseArchetype);
ASSERT_FLOAT_EQ(movedArchetype->Container<DestroyableComponent>()[0].GetMaxHealth(), 30);
ASSERT_EQ(movedArchetype->Container<SimplePhysicsComponent>()[0].GetPosition(), NiPoint3(1.0f, 2.0f, 3.0f));
std::unique_ptr<ArchetypeBase> movedArchetype = std::move(baseArchetype);
ASSERT_FLOAT_EQ(movedArchetype->GetContainer<DestroyableComponent>()[0].GetMaxHealth(), 30);
ASSERT_EQ(movedArchetype->GetContainer<SimplePhysicsComponent>()[0].GetPosition(), NiPoint3(1.0f, 2.0f, 3.0f));
ASSERT_EQ(baseArchetype.get(), nullptr);
}

Expand Down Expand Up @@ -287,11 +287,11 @@ namespace {

template <typename T>
struct ContainerVisitor {
std::vector<T>* const operator()(auto&& archetype) {
Container<T>::storage_type* const operator()(auto&& archetype) {
using ArchetypeType = std::remove_pointer_t<std::remove_reference_t<decltype(*archetype)>>; // Needed to fix a MacOS issue

if constexpr (!ArchetypeType::template HasComponent<T>()) return nullptr;
else return &archetype->template Container<T>();
else return &archetype->template Container<T>::entries;
}
};
}
Expand Down
3 changes: 0 additions & 3 deletions thirdparty/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ add_subdirectory(SQLite)
# Source code for magic_enum
add_subdirectory(magic_enum)

# Source code for MD5
add_subdirectory(MD5)

# MariaDB C++ Connector
include(CMakeMariaDBLists.txt)

Expand Down
5 changes: 0 additions & 5 deletions thirdparty/MD5/CMakeLists.txt

This file was deleted.

0 comments on commit 6d1d7fc

Please sign in to comment.