WIP: improvements and new features

This commit is contained in:
Michele Caini
2017-08-30 13:06:30 +02:00
parent 8de75fcfab
commit af70573634
11 changed files with 562 additions and 502 deletions

View File

@@ -6,6 +6,9 @@
# Introduction
WIP. Documentation is coming soon.
<!--
`EnTT` is a header-only, tiny and easy to use Entity-Component System in modern C++.<br/>
ECS is an architectural pattern used mostly in game development. For further details:
@@ -347,3 +350,4 @@ Check the [contributors list](https://github.com/skypjack/entt/blob/master/AUTHO
Code and documentation Copyright (c) 2017 Michele Caini.<br/>
Code released under [the MIT license](https://github.com/skypjack/entt/blob/master/LICENSE).
-->

View File

@@ -1,235 +0,0 @@
#ifndef ENTT_COMPONENT_POOL_HPP
#define ENTT_COMPONENT_POOL_HPP
#include <utility>
#include <vector>
#include <cassert>
namespace entt {
template<typename, typename, typename...>
class ComponentPool;
template<typename Entity, typename Component>
class ComponentPool<Entity, Component> {
public:
using component_type = Component;
using entity_type = Entity;
using pos_type = entity_type;
using size_type = typename std::vector<component_type>::size_type;
using iterator_type = typename std::vector<entity_type>::iterator;
using const_iterator_type = typename std::vector<entity_type>::const_iterator;
private:
inline bool valid(entity_type entity) const noexcept {
return entity < reverse.size() && reverse[entity] < direct.size() && direct[reverse[entity]] == entity;
}
public:
explicit ComponentPool(size_type dim = 4098) noexcept {
assert(!(dim < 0));
data.reserve(dim);
}
ComponentPool(ComponentPool &&) = default;
~ComponentPool() noexcept {
assert(empty());
}
ComponentPool & operator=(ComponentPool &&) = default;
bool empty() const noexcept {
return data.empty();
}
size_type capacity() const noexcept {
return data.capacity();
}
size_type size() const noexcept {
return data.size();
}
iterator_type begin() noexcept {
return direct.begin();
}
const_iterator_type cbegin() const noexcept {
return direct.cbegin();
}
iterator_type end() noexcept {
return direct.end();
}
const_iterator_type cend() const noexcept {
return direct.cend();
}
bool has(entity_type entity) const noexcept {
return valid(entity);
}
const component_type & get(entity_type entity) const noexcept {
assert(valid(entity));
return data[reverse[entity]];
}
component_type & get(entity_type entity) noexcept {
return const_cast<component_type &>(const_cast<const ComponentPool *>(this)->get(entity));
}
template<typename... Args>
component_type & construct(entity_type entity, Args... args) {
assert(!valid(entity));
if(!(entity < reverse.size())) {
reverse.resize(entity+1);
}
reverse[entity] = pos_type(direct.size());
direct.emplace_back(entity);
data.push_back({ args... });
return data.back();
}
void destroy(entity_type entity) {
assert(valid(entity));
auto last = direct.size() - 1;
reverse[direct[last]] = reverse[entity];
direct[reverse[entity]] = direct[last];
data[reverse[entity]] = std::move(data[last]);
direct.pop_back();
data.pop_back();
}
void reset() {
data.clear();
reverse.resize(0);
direct.clear();
}
private:
std::vector<component_type> data;
std::vector<pos_type> reverse;
std::vector<entity_type> direct;
};
template<typename Entity, typename Component, typename... Components>
class ComponentPool
: ComponentPool<Entity, Component>, ComponentPool<Entity, Components>...
{
template<typename Comp>
using Pool = ComponentPool<Entity, Comp>;
public:
using entity_type = typename Pool<Component>::entity_type;
using pos_type = typename Pool<Component>::pos_type;
using size_type = typename Pool<Component>::size_type;
using iterator_type = typename Pool<Component>::iterator_type;
using const_iterator_type = typename Pool<Component>::const_iterator_type;
explicit ComponentPool(size_type dim = 4098) noexcept
#ifdef _MSC_VER
: ComponentPool<Entity, Component>{dim}, ComponentPool<Entity, Components>{dim}...
#else
: Pool<Component>{dim}, Pool<Components>{dim}...
#endif
{
assert(!(dim < 0));
}
ComponentPool(const ComponentPool &) = delete;
ComponentPool(ComponentPool &&) = delete;
ComponentPool & operator=(const ComponentPool &) = delete;
ComponentPool & operator=(ComponentPool &&) = delete;
template<typename Comp>
bool empty() const noexcept {
return Pool<Comp>::empty();
}
template<typename Comp>
size_type capacity() const noexcept {
return Pool<Comp>::capacity();
}
template<typename Comp>
size_type size() const noexcept {
return Pool<Comp>::size();
}
template<typename Comp>
iterator_type begin() noexcept {
return Pool<Comp>::begin();
}
template<typename Comp>
const_iterator_type cbegin() const noexcept {
return Pool<Comp>::cbegin();
}
template<typename Comp>
iterator_type end() noexcept {
return Pool<Comp>::end();
}
template<typename Comp>
const_iterator_type cend() const noexcept {
return Pool<Comp>::cend();
}
template<typename Comp>
bool has(entity_type entity) const noexcept {
return Pool<Comp>::has(entity);
}
template<typename Comp>
const Comp & get(entity_type entity) const noexcept {
return Pool<Comp>::get(entity);
}
template<typename Comp>
Comp & get(entity_type entity) noexcept {
return const_cast<Comp &>(const_cast<const ComponentPool *>(this)->get<Comp>(entity));
}
template<typename Comp, typename... Args>
Comp & construct(entity_type entity, Args... args) {
return Pool<Comp>::construct(entity, args...);
}
template<typename Comp>
void destroy(entity_type entity) {
Pool<Comp>::destroy(entity);
}
template<typename Comp>
void reset() {
Pool<Comp>::reset();
}
void reset() {
using accumulator_type = int[];
Pool<Component>::reset();
accumulator_type accumulator = { (Pool<Components>::reset(), 0)... };
(void)accumulator;
}
};
}
#endif // ENTT_COMPONENT_POOL_HPP

View File

@@ -2,6 +2,7 @@
#define ENTT_REGISTRY_HPP
#include <tuple>
#include <vector>
#include <bitset>
#include <utility>
@@ -9,30 +10,29 @@
#include <iterator>
#include <cassert>
#include <type_traits>
#include "component_pool.hpp"
#include "sparse_set.hpp"
#include "ident.hpp"
namespace entt {
template<typename...>
template<typename, std::size_t...>
class View;
template<template<typename...> class Pool, typename Entity, typename... Components, typename Type, typename... Types>
class View<Pool<Entity, Components...>, Type, Types...> final {
using pool_type = Pool<Entity, Components...>;
using entity_type = typename pool_type::entity_type;
using mask_type = std::bitset<sizeof...(Components)+1>;
using underlying_iterator_type = typename pool_type::const_iterator_type;
template<typename Pool, std::size_t Ident, std::size_t... Other>
class View<Pool, Ident, Other...> final {
using pool_type = Pool;
using mask_type = std::bitset<std::tuple_size<Pool>::value + 1>;
using underlying_iterator_type = typename std::tuple_element_t<Ident, Pool>::iterator_type;
class ViewIterator;
public:
using iterator_type = ViewIterator;
using const_iterator_type = iterator_type;
using size_type = typename pool_type::size_type;
using entity_type = typename std::tuple_element_t<Ident, Pool>::index_type;
using size_type = typename std::tuple_element_t<Ident, Pool>::size_type;
private:
class ViewIterator {
@@ -42,10 +42,6 @@ private:
public:
using value_type = entity_type;
using difference_type = std::ptrdiff_t;
using reference = entity_type &;
using pointer = entity_type *;
using iterator_category = std::input_iterator_tag;
ViewIterator(underlying_iterator_type begin, underlying_iterator_type end, const mask_type &bitmask, const mask_type *mask) noexcept
: begin{begin}, end{end}, bitmask{bitmask}, mask{mask}
@@ -85,133 +81,137 @@ private:
const mask_type *mask;
};
template<typename Comp>
template<std::size_t Idx>
void prefer(size_type &size) noexcept {
auto sz = pool.template size<Comp>();
auto &&cpool = std::get<Idx>(*pool);
auto sz = cpool.size();
if(sz < size) {
from = pool.template begin<Type>();
to = pool.template end<Type>();
from = cpool.begin();
to = cpool.end();
size = sz;
}
}
public:
explicit View(pool_type &pool, const mask_type *mask) noexcept
: from{pool.template begin<Type>()},
to{pool.template end<Type>()},
explicit View(const pool_type *pool, const mask_type *mask) noexcept
: from{std::get<Ident>(*pool).begin()},
to{std::get<Ident>(*pool).end()},
pool{pool},
mask{mask}
{
using accumulator_type = int[];
size_type size = pool.template size<Type>();
bitmask.set(ident<Components...>.template get<Type>());
accumulator_type types = { 0, (bitmask.set(ident<Components...>.template get<Types>()), 0)... };
accumulator_type pref = { 0, (prefer<Types>(size), 0)... };
size_type size = std::get<Ident>(*pool).size();
bitmask.set(Ident);
accumulator_type types = { 0, (bitmask.set(Other), 0)... };
accumulator_type pref = { 0, (prefer<Other>(size), 0)... };
(void)types, (void)pref;
}
const_iterator_type begin() const noexcept {
iterator_type begin() const noexcept {
return ViewIterator{from, to, bitmask, mask};
}
iterator_type begin() noexcept {
return const_cast<const View *>(this)->begin();
}
const_iterator_type end() const noexcept {
iterator_type end() const noexcept {
return ViewIterator{to, to, bitmask, mask};
}
iterator_type end() noexcept {
return const_cast<const View *>(this)->end();
}
void reset() noexcept {
using accumulator_type = int[];
from = pool.template begin<Type>();
to = pool.template end<Type>();
size_type size = pool.template size<Type>();
accumulator_type accumulator = { 0, (prefer<Types>(size), 0)... };
auto &&cpool = std::get<Ident>(*pool);
from = cpool.begin();
to = cpool.end();
size_type size = cpool.size();
accumulator_type accumulator = { 0, (prefer<Other>(size), 0)... };
(void)accumulator;
}
private:
underlying_iterator_type from;
underlying_iterator_type to;
pool_type &pool;
const pool_type *pool;
const mask_type *mask;
mask_type bitmask;
};
template<template<typename...> class Pool, typename Entity, typename... Components, typename Type>
class View<Pool<Entity, Components...>, Type> final {
using pool_type = Pool<Entity, Components...>;
template<typename Pool, std::size_t Ident>
class View<Pool, Ident> final {
using pool_type = std::tuple_element_t<Ident, Pool>;
public:
using iterator_type = typename pool_type::iterator_type;
using entity_type = typename pool_type::index_type;
using size_type = typename pool_type::size_type;
using iterator_type = typename pool_type::const_iterator_type;
using const_iterator_type = iterator_type;
using raw_type = typename pool_type::type;
explicit View(pool_type &pool) noexcept
: pool{pool}
explicit View(const Pool *pool) noexcept
: pool{&std::get<Ident>(*pool)}
{}
const_iterator_type cbegin() const noexcept {
return pool.template cbegin<Type>();
raw_type * raw() noexcept {
return pool->raw();
}
iterator_type begin() noexcept {
return pool.template begin<Type>();
const raw_type * raw() const noexcept {
return pool->raw();
}
const_iterator_type cend() const noexcept {
return pool.template cend<Type>();
}
iterator_type end() noexcept {
return pool.template end<Type>();
const entity_type * data() const noexcept {
return pool->data();
}
size_type size() const noexcept {
return pool.template size<Type>();
return pool->size();
}
iterator_type begin() const noexcept {
return pool->begin();
}
iterator_type end() const noexcept {
return pool->end();
}
private:
pool_type &pool;
const pool_type *pool;
};
template<typename>
template<typename...>
class Registry;
template<template<typename...> class Pool, typename Entity, typename... Components>
class Registry<Pool<Entity, Components...>> {
static_assert(sizeof...(Components) > 1, "!");
template<template<typename...> class... Pool, typename Entity, typename... Component>
class Registry<Pool<Entity, Component>...> {
using pool_type = std::tuple<Pool<Entity, Component>...>;
using mask_type = std::bitset<sizeof...(Component)+1>;
using pool_type = Pool<Entity, Components...>;
using mask_type = std::bitset<sizeof...(Components)+1>;
static constexpr auto validity_bit = sizeof...(Components);
static constexpr auto validity_bit = sizeof...(Component);
public:
using entity_type = typename pool_type::entity_type;
using entity_type = Entity;
using size_type = typename std::vector<mask_type>::size_type;
template<typename... Comp>
using view_type = View<pool_type, ident<Component...>.template get<Comp>()...>;
private:
template<typename Comp>
void clone(entity_type to, entity_type from) {
if(entities[from].test(ident<Components...>.template get<Comp>())) {
assign<Comp>(to, pool.template get<Comp>(from));
constexpr auto index = ident<Component...>.template get<Comp>();
if(entities[from].test(index)) {
assign<Comp>(to, std::get<index>(pool).get(from));
}
}
template<typename Comp>
void sync(entity_type to, entity_type from) {
bool src = entities[from].test(ident<Components...>.template get<Comp>());
bool dst = entities[to].test(ident<Components...>.template get<Comp>());
constexpr auto index = ident<Component...>.template get<Comp>();
bool src = entities[from].test(index);
bool dst = entities[to].test(index);
if(src && dst) {
copy<Comp>(to, from);
@@ -223,9 +223,6 @@ private:
}
public:
template<typename... Comp>
using view_type = View<pool_type, Comp...>;
template<typename... Args>
Registry(Args&&... args)
: pool{std::forward<Args>(args)...}
@@ -247,7 +244,8 @@ public:
template<typename Comp>
bool empty() const noexcept {
return pool.template empty<Comp>();
constexpr auto index = ident<Component...>.template get<Comp>();
return std::get<index>(pool).empty();
}
bool empty() const noexcept {
@@ -286,7 +284,7 @@ public:
void destroy(entity_type entity) {
assert(valid(entity));
using accumulator_type = int[];
accumulator_type accumulator = { 0, (reset<Components>(entity), 0)... };
accumulator_type accumulator = { 0, (reset<Component>(entity), 0)... };
available.push_back(entity);
entities[entity].reset();
(void)accumulator;
@@ -295,15 +293,17 @@ public:
template<typename Comp, typename... Args>
Comp & assign(entity_type entity, Args... args) {
assert(valid(entity));
entities[entity].set(ident<Components...>.template get<Comp>());
return pool.template construct<Comp>(entity, args...);
constexpr auto index = ident<Component...>.template get<Comp>();
entities[entity].set(index);
return std::get<index>(pool).construct(entity, args...);
}
template<typename Comp>
void remove(entity_type entity) {
assert(valid(entity));
entities[entity].reset(ident<Components...>.template get<Comp>());
pool.template destroy<Comp>(entity);
constexpr auto index = ident<Component...>.template get<Comp>();
entities[entity].reset(index);
std::get<index>(pool).destroy(entity);
}
template<typename... Comp>
@@ -312,31 +312,36 @@ public:
using accumulator_type = bool[];
bool all = true;
auto &mask = entities[entity];
accumulator_type accumulator = { true, (all = all && mask.test(ident<Components...>.template get<Comp>()))... };
accumulator_type accumulator = { true, (all = all && mask.test(ident<Component...>.template get<Comp>()))... };
(void)accumulator;
return all;
}
template<typename Comp>
const Comp & get(entity_type entity) const noexcept {
return pool.template get<Comp>(entity);
constexpr auto index = ident<Component...>.template get<Comp>();
return std::get<index>(pool).get(entity);
}
template<typename Comp>
Comp & get(entity_type entity) noexcept {
return pool.template get<Comp>(entity);
constexpr auto index = ident<Component...>.template get<Comp>();
return std::get<index>(pool).get(entity);
}
template<typename Comp, typename... Args>
Comp & replace(entity_type entity, Args... args) {
return (pool.template get<Comp>(entity) = Comp{args...});
constexpr auto index = ident<Component...>.template get<Comp>();
return (std::get<index>(pool).get(entity) = Comp{args...});
}
template<typename Comp, typename... Args>
Comp & accomodate(entity_type entity, Args... args) {
assert(valid(entity));
return (entities[entity].test(ident<Components...>.template get<Comp>())
constexpr auto index = ident<Component...>.template get<Comp>();
return (entities[entity].test(index)
? this->template replace<Comp>(entity, std::forward<Args>(args)...)
: this->template assign<Comp>(entity, std::forward<Args>(args)...));
}
@@ -345,19 +350,21 @@ public:
assert(valid(from));
using accumulator_type = int[];
auto to = create();
accumulator_type accumulator = { 0, (clone<Components>(to, from), 0)... };
accumulator_type accumulator = { 0, (clone<Component>(to, from), 0)... };
(void)accumulator;
return to;
}
template<typename Comp>
Comp & copy(entity_type to, entity_type from) {
return (pool.template get<Comp>(to) = pool.template get<Comp>(from));
constexpr auto index = ident<Component...>.template get<Comp>();
auto &&cpool = std::get<index>(pool);
return (cpool.get(to) = cpool.get(from));
}
void copy(entity_type to, entity_type from) {
using accumulator_type = int[];
accumulator_type accumulator = { 0, (sync<Components>(to, from), 0)... };
accumulator_type accumulator = { 0, (sync<Component>(to, from), 0)... };
(void)accumulator;
}
@@ -365,33 +372,39 @@ public:
void reset(entity_type entity) {
assert(valid(entity));
if(entities[entity].test(ident<Components...>.template get<Comp>())) {
constexpr auto index = ident<Component...>.template get<Comp>();
if(entities[entity].test(index)) {
remove<Comp>(entity);
}
}
template<typename Comp>
void reset() {
constexpr auto index = ident<Component...>.template get<Comp>();
for(entity_type entity = 0, last = entity_type(entities.size()); entity < last; ++entity) {
if(entities[entity].test(ident<Components...>.template get<Comp>())) {
if(entities[entity].test(index)) {
remove<Comp>(entity);
}
}
}
void reset() {
using accumulator_type = int[];
accumulator_type acc = { 0, (std::get<ident<Component...>.template get<Component>()>(pool).reset(), 0)... };
entities.clear();
available.clear();
pool.reset();
(void)acc;
}
template<typename... Comp>
std::enable_if_t<(sizeof...(Comp) == 1), view_type<Comp...>>
view() noexcept { return view_type<Comp...>{pool}; }
view() noexcept { return view_type<Comp...>{&pool}; }
template<typename... Comp>
std::enable_if_t<(sizeof...(Comp) > 1), view_type<Comp...>>
view() noexcept { return view_type<Comp...>{pool, entities.data()}; }
view() noexcept { return view_type<Comp...>{&pool, entities.data()}; }
private:
std::vector<mask_type> entities;
@@ -400,12 +413,12 @@ private:
};
template<typename Entity, typename... Components>
using StandardRegistry = Registry<ComponentPool<Entity, Components...>>;
template<typename Entity, typename... Component>
using StandardRegistry = Registry<SparseSet<Entity, Component>...>;
template<typename... Components>
using DefaultRegistry = Registry<ComponentPool<std::uint32_t, Components...>>;
template<typename... Component>
using DefaultRegistry = Registry<SparseSet<std::uint32_t, Component>...>;
}

205
src/sparse_set.hpp Normal file
View File

@@ -0,0 +1,205 @@
#ifndef ENTT_COMPONENT_POOL_HPP
#define ENTT_COMPONENT_POOL_HPP
#include <utility>
#include <vector>
#include <cstddef>
#include <cassert>
namespace entt {
template<typename...>
class SparseSet;
template<typename Index>
class SparseSet<Index> {
struct SparseSetIterator;
public:
using index_type = Index;
using pos_type = index_type;
using size_type = std::size_t;
using iterator_type = SparseSetIterator;
private:
struct SparseSetIterator {
using value_type = index_type;
SparseSetIterator(const std::vector<index_type> *direct, size_type pos)
: direct{direct}, pos{pos}
{}
SparseSetIterator & operator++() noexcept {
return --pos, *this;
}
SparseSetIterator operator++(int) noexcept {
SparseSetIterator orig = *this;
return ++(*this), orig;
}
bool operator==(const SparseSetIterator &other) const noexcept {
return other.pos == pos && other.direct == direct;
}
bool operator!=(const SparseSetIterator &other) const noexcept {
return !(*this == other);
}
value_type operator*() const noexcept {
return (*direct)[pos-1];
}
private:
const std::vector<index_type> *direct;
size_type pos;
};
inline bool valid(Index idx) const noexcept {
return idx < reverse.size() && reverse[idx] < direct.size() && direct[reverse[idx]] == idx;
}
public:
explicit SparseSet() = default;
SparseSet(const SparseSet &) = delete;
SparseSet(SparseSet &&) = default;
virtual ~SparseSet() noexcept {
assert(empty());
}
SparseSet & operator=(const SparseSet &) = delete;
SparseSet & operator=(SparseSet &&) = default;
bool empty() const noexcept {
return direct.empty();
}
const index_type * data() const noexcept {
return direct.data();
}
size_type size() const noexcept {
return direct.size();
}
iterator_type begin() const noexcept {
return SparseSetIterator{&direct, direct.size()};
}
iterator_type end() const noexcept {
return SparseSetIterator{&direct, 0};
}
bool has(index_type idx) const noexcept {
return valid(idx);
}
pos_type get(index_type idx) const noexcept {
assert(valid(idx));
return reverse[idx];
}
pos_type construct(index_type idx) {
assert(!valid(idx));
if(!(idx < reverse.size())) {
reverse.resize(idx+1);
}
auto pos = pos_type(direct.size());
reverse[idx] = pos;
direct.emplace_back(idx);
return pos;
}
pos_type destroy(index_type idx) {
assert(valid(idx));
auto last = direct.size() - 1;
auto pos = reverse[idx];
reverse[direct[last]] = pos;
direct[pos] = direct[last];
direct.pop_back();
return pos;
}
void reset() {
reverse.resize(0);
direct.clear();
}
private:
std::vector<pos_type> reverse;
std::vector<index_type> direct;
};
template<typename Index, typename Type>
class SparseSet<Index, Type> final: public SparseSet<Index> {
public:
using type = Type;
using index_type = typename SparseSet<Index>::index_type;
using pos_type = typename SparseSet<Index>::pos_type;
using size_type = typename SparseSet<Index>::size_type;
using iterator_type = typename SparseSet<Index>::iterator_type;
explicit SparseSet() = default;
SparseSet(const SparseSet &) = delete;
SparseSet(SparseSet &&) = default;
SparseSet & operator=(const SparseSet &) = delete;
SparseSet & operator=(SparseSet &&) = default;
type * raw() noexcept {
return instances.data();
}
const type * raw() const noexcept {
return instances.data();
}
const type & get(index_type idx) const noexcept {
return instances[SparseSet<Index>::get(idx)];
}
type & get(index_type idx) noexcept {
return const_cast<type &>(const_cast<const SparseSet *>(this)->get(idx));
}
template<typename... Args>
type & construct(index_type idx, Args... args) {
SparseSet<Index>::construct(idx);
instances.push_back({ args... });
return instances.back();
}
void destroy(index_type idx) {
auto pos = SparseSet<Index>::destroy(idx);
instances[pos] = std::move(instances[SparseSet<Index>::size()]);
instances.pop_back();
}
void reset() {
SparseSet<Index>::reset();
instances.clear();
}
private:
std::vector<type> instances;
};
}
#endif // ENTT_COMPONENT_POOL_HPP

97
src/tag_handler.hpp Normal file
View File

@@ -0,0 +1,97 @@
#ifndef ENTT_TAG_HANDLER_HPP
#define ENTT_TAG_HANDLER_HPP
#include <type_traits>
#include <utility>
#include <cstddef>
#include <cassert>
namespace entt {
template<typename Index, typename Type>
struct TagHandler {
using type = Type;
using index_type = Index;
using pos_type = index_type;
using size_type = std::size_t;
using iterator_type = const type *;
private:
inline bool valid(index_type idx) const noexcept {
return !idx && tag;
}
public:
explicit TagHandler() = default;
TagHandler(const TagHandler &) = delete;
TagHandler(TagHandler &&) = default;
~TagHandler() noexcept {
assert(not tag);
}
TagHandler & operator=(const TagHandler &) = delete;
TagHandler & operator=(TagHandler &&) = default;
bool empty() const noexcept {
return !tag;
}
size_type size() const noexcept {
return tag ? size_type{1} : size_type{0};
}
iterator_type begin() const noexcept {
return tag;
}
iterator_type end() const noexcept {
return tag ? tag + 1 : tag;
}
bool has(index_type idx) const noexcept {
return valid(idx);
}
const type & get(index_type idx) const noexcept {
assert(valid(idx));
return *tag;
}
type & get(index_type idx) noexcept {
return const_cast<type &>(const_cast<const TagHandler *>(this)->get(idx));
}
template<typename... Args>
type & construct(index_type idx, Args... args) {
assert(!valid(idx));
tag = new(&chunk) Type{std::forward<Args>(args)...};
return *tag;
}
void destroy(index_type idx) {
assert(valid(idx));
tag->~Type();
tag = nullptr;
}
void reset() {
if(tag) {
destroy(index_type{0});
}
}
private:
std::aligned_storage_t<sizeof(Type), alignof(Type)> chunk;
Type *tag{nullptr};
};
}
#endif // ENTT_TAG_HANDLER_HPP

View File

@@ -11,7 +11,7 @@ set(TARGET_BENCHMARK benchmark)
# Test TARGET_ENTT
add_executable(${TARGET_ENTT} component_pool.cpp registry.cpp)
add_executable(${TARGET_ENTT} registry.cpp sparse_set.cpp tag_handler.cpp)
target_include_directories(${TARGET_ENTT} PRIVATE ${PROJECT_SRC_DIR})
target_link_libraries(${TARGET_ENTT} PRIVATE ${COMMON_LINK_LIBS})
add_test(NAME ${TARGET_ENTT} COMMAND ${TARGET_ENTT})

View File

@@ -38,6 +38,7 @@ TEST(DefaultRegistry, Construct) {
std::cout << "Constructing 10000000 entities" << std::endl;
Timer timer;
for (uint64_t i = 0; i < 10000000L; i++) {
registry.create();
}

View File

@@ -1,166 +0,0 @@
#include <cstddef>
#include <gtest/gtest.h>
#include <component_pool.hpp>
TEST(ComponentPool, Functionalities) {
using pool_type = entt::ComponentPool<std::uint8_t, int, double>;
pool_type pool{0};
ASSERT_TRUE(pool.empty<int>());
ASSERT_TRUE(pool.empty<double>());
ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{0});
ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{0});
ASSERT_EQ(pool.size<int>(), pool_type::size_type{0});
ASSERT_EQ(pool.size<double>(), pool_type::size_type{0});
ASSERT_EQ(pool.begin<int>(), pool.end<int>());
ASSERT_EQ(pool.begin<double>(), pool.end<double>());
ASSERT_FALSE(pool.has<int>(0));
ASSERT_FALSE(pool.has<double>(0));
}
TEST(ComponentPool, ConstructDestroy) {
using pool_type = entt::ComponentPool<std::uint8_t, double, int>;
pool_type pool{4};
ASSERT_EQ(pool.construct<int>(0, 42), 42);
ASSERT_FALSE(pool.empty<int>());
ASSERT_TRUE(pool.empty<double>());
ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{4});
ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{4});
ASSERT_EQ(pool.size<int>(), pool_type::size_type{1});
ASSERT_EQ(pool.size<double>(), pool_type::size_type{0});
ASSERT_TRUE(pool.has<int>(0));
ASSERT_FALSE(pool.has<double>(0));
ASSERT_FALSE(pool.has<int>(1));
ASSERT_FALSE(pool.has<double>(1));
ASSERT_EQ(pool.construct<int>(1), 0);
ASSERT_FALSE(pool.empty<int>());
ASSERT_TRUE(pool.empty<double>());
ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{4});
ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{4});
ASSERT_EQ(pool.size<int>(), pool_type::size_type{2});
ASSERT_EQ(pool.size<double>(), pool_type::size_type{0});
ASSERT_TRUE(pool.has<int>(0));
ASSERT_FALSE(pool.has<double>(0));
ASSERT_TRUE(pool.has<int>(1));
ASSERT_FALSE(pool.has<double>(1));
ASSERT_NE(pool.get<int>(0), pool.get<int>(1));
ASSERT_NE(&pool.get<int>(0), &pool.get<int>(1));
ASSERT_NO_THROW(pool.destroy<int>(0));
ASSERT_FALSE(pool.empty<int>());
ASSERT_TRUE(pool.empty<double>());
ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{4});
ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{4});
ASSERT_EQ(pool.size<int>(), pool_type::size_type{1});
ASSERT_EQ(pool.size<double>(), pool_type::size_type{0});
ASSERT_FALSE(pool.has<int>(0));
ASSERT_FALSE(pool.has<double>(0));
ASSERT_TRUE(pool.has<int>(1));
ASSERT_FALSE(pool.has<double>(1));
ASSERT_NO_THROW(pool.destroy<int>(1));
ASSERT_TRUE(pool.empty<int>());
ASSERT_TRUE(pool.empty<double>());
ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{4});
ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{4});
ASSERT_EQ(pool.size<int>(), pool_type::size_type{0});
ASSERT_EQ(pool.size<int>(), pool_type::size_type{0});
ASSERT_FALSE(pool.has<int>(0));
ASSERT_FALSE(pool.has<double>(0));
ASSERT_FALSE(pool.has<int>(1));
ASSERT_FALSE(pool.has<double>(1));
int *comp[] = {
&pool.construct<int>(0, 0),
&pool.construct<int>(1, 1),
nullptr,
&pool.construct<int>(3, 3)
};
ASSERT_FALSE(pool.empty<int>());
ASSERT_TRUE(pool.empty<double>());
ASSERT_EQ(pool.capacity<int>(), pool_type::size_type{4});
ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{4});
ASSERT_EQ(pool.size<int>(), pool_type::size_type{3});
ASSERT_EQ(pool.size<double>(), pool_type::size_type{0});
ASSERT_TRUE(pool.has<int>(0));
ASSERT_FALSE(pool.has<double>(0));
ASSERT_TRUE(pool.has<int>(1));
ASSERT_FALSE(pool.has<double>(1));
ASSERT_FALSE(pool.has<int>(2));
ASSERT_FALSE(pool.has<double>(2));
ASSERT_TRUE(pool.has<int>(3));
ASSERT_FALSE(pool.has<double>(3));
ASSERT_EQ(&pool.get<int>(0), comp[0]);
ASSERT_EQ(&pool.get<int>(1), comp[1]);
ASSERT_EQ(&pool.get<int>(3), comp[3]);
ASSERT_EQ(pool.get<int>(0), 0);
ASSERT_EQ(pool.get<int>(1), 1);
ASSERT_EQ(pool.get<int>(3), 3);
ASSERT_NO_THROW(pool.destroy<int>(0));
ASSERT_NO_THROW(pool.destroy<int>(1));
ASSERT_NO_THROW(pool.destroy<int>(3));
}
TEST(ComponentPool, HasGet) {
using pool_type = entt::ComponentPool<std::uint8_t, int, char>;
pool_type pool;
const pool_type &cpool = pool;
int &comp = pool.construct<int>(0, 42);
ASSERT_EQ(pool.get<int>(0), comp);
ASSERT_EQ(pool.get<int>(0), 42);
ASSERT_TRUE(pool.has<int>(0));
ASSERT_EQ(cpool.get<int>(0), comp);
ASSERT_EQ(cpool.get<int>(0), 42);
ASSERT_TRUE(cpool.has<int>(0));
ASSERT_NO_THROW(pool.destroy<int>(0));
}
TEST(ComponentPool, BeginEndReset) {
using pool_type = entt::ComponentPool<std::uint8_t, int, char>;
pool_type pool{2};
ASSERT_EQ(pool.construct<int>(0, 0), 0);
ASSERT_EQ(pool.construct<int>(2, 2), 2);
ASSERT_EQ(pool.construct<int>(3, 3), 3);
ASSERT_EQ(pool.construct<int>(1, 1), 1);
ASSERT_EQ(pool.size<int>(), decltype(pool.size<int>()){4});
ASSERT_EQ(*(pool.begin<int>()+0), typename pool_type::entity_type{0});
ASSERT_EQ(*(pool.begin<int>()+1), typename pool_type::entity_type{2});
ASSERT_EQ(*(pool.begin<int>()+2), typename pool_type::entity_type{3});
ASSERT_EQ(*(pool.begin<int>()+3), typename pool_type::entity_type{1});
pool.destroy<int>(2);
ASSERT_EQ(pool.size<int>(), decltype(pool.size<int>()){3});
ASSERT_EQ(*(pool.begin<int>()+0), typename pool_type::entity_type{0});
ASSERT_EQ(*(pool.begin<int>()+1), typename pool_type::entity_type{1});
ASSERT_EQ(*(pool.begin<int>()+2), typename pool_type::entity_type{3});
ASSERT_EQ(pool.construct<char>(0, 'c'), 'c');
ASSERT_FALSE(pool.empty<int>());
ASSERT_FALSE(pool.empty<char>());
ASSERT_NO_THROW(pool.reset<char>());
ASSERT_FALSE(pool.empty<int>());
ASSERT_TRUE(pool.empty<char>());
ASSERT_NO_THROW(pool.reset());
ASSERT_TRUE(pool.empty<int>());
ASSERT_TRUE(pool.empty<char>());
}

View File

@@ -199,7 +199,7 @@ TEST(DefaultRegistry, ViewMultipleComponent) {
ASSERT_NO_THROW(registry.reset());
}
TEST(DefaultRegistry, EmptyViewSingleComponent) {
TEST(DefaultRegistry, ViewSingleComponentEmpty) {
using registry_type = entt::DefaultRegistry<char, int, double>;
registry_type registry;
@@ -211,10 +211,15 @@ TEST(DefaultRegistry, EmptyViewSingleComponent) {
ASSERT_EQ(view.size(), registry_type::size_type{0});
for(auto entity: view) {
(void)entity;
FAIL();
}
registry.reset();
}
TEST(DefaultRegistry, EmptyViewMultipleComponent) {
TEST(DefaultRegistry, ViewMultipleComponentEmpty) {
using registry_type = entt::DefaultRegistry<char, int, float, double>;
registry_type registry;

90
test/sparse_set.cpp Normal file
View File

@@ -0,0 +1,90 @@
#include <gtest/gtest.h>
#include <sparse_set.hpp>
TEST(SparseSetNoType, Functionalities) {
using SparseSet = entt::SparseSet<unsigned int>;
SparseSet set;
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.begin(), set.end());
ASSERT_FALSE(set.has(0));
ASSERT_FALSE(set.has(42));
ASSERT_EQ(set.construct(42), 0u);
ASSERT_FALSE(set.empty());
ASSERT_EQ(set.size(), 1u);
ASSERT_NE(set.begin(), set.end());
ASSERT_FALSE(set.has(0));
ASSERT_TRUE(set.has(42));
auto begin = set.begin();
ASSERT_EQ(*begin, 42u);
ASSERT_EQ(++begin, set.end());
ASSERT_EQ(set.get(42), 0u);
ASSERT_EQ(set.destroy(42), 0u);
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.begin(), set.end());
ASSERT_FALSE(set.has(0));
ASSERT_FALSE(set.has(42));
ASSERT_EQ(set.construct(42), 0u);
set.reset();
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.begin(), set.end());
ASSERT_FALSE(set.has(0));
ASSERT_FALSE(set.has(42));
}
TEST(SparseSetWithType, Functionalities) {
using SparseSet = entt::SparseSet<unsigned int, int>;
SparseSet set;
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.begin(), set.end());
ASSERT_FALSE(set.has(0));
ASSERT_FALSE(set.has(42));
ASSERT_EQ(set.construct(42, 3), 3);
ASSERT_FALSE(set.empty());
ASSERT_EQ(set.size(), 1u);
ASSERT_NE(set.begin(), set.end());
ASSERT_FALSE(set.has(0));
ASSERT_TRUE(set.has(42));
auto begin = set.begin();
ASSERT_EQ(*begin, 42u);
ASSERT_EQ(set.get(42), 3);
ASSERT_EQ(++begin, set.end());
set.destroy(42);
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.begin(), set.end());
ASSERT_FALSE(set.has(0));
ASSERT_FALSE(set.has(42));
ASSERT_EQ(set.construct(42, 12), 12);
set.reset();
ASSERT_TRUE(set.empty());
ASSERT_EQ(set.size(), 0u);
ASSERT_EQ(set.begin(), set.end());
ASSERT_FALSE(set.has(0));
ASSERT_FALSE(set.has(42));
}

46
test/tag_handler.cpp Normal file
View File

@@ -0,0 +1,46 @@
#include <gtest/gtest.h>
#include <tag_handler.hpp>
TEST(TagHandler, Functionalities) {
using TagHandler = entt::TagHandler<unsigned int, int>;
TagHandler handler;
ASSERT_TRUE(handler.empty());
ASSERT_EQ(handler.size(), 0u);
ASSERT_EQ(handler.begin(), handler.end());
ASSERT_FALSE(handler.has(0));
ASSERT_FALSE(handler.has(1));
ASSERT_EQ(handler.construct(0, 42), 42);
ASSERT_FALSE(handler.empty());
ASSERT_EQ(handler.size(), 1u);
ASSERT_NE(handler.begin(), handler.end());
ASSERT_TRUE(handler.has(0));
ASSERT_FALSE(handler.has(1));
auto begin = handler.begin();
ASSERT_EQ(*begin, 42);
ASSERT_EQ(handler.get(0), 42);
ASSERT_EQ(++begin, handler.end());
handler.destroy(0);
ASSERT_TRUE(handler.empty());
ASSERT_EQ(handler.size(), 0u);
ASSERT_EQ(handler.begin(), handler.end());
ASSERT_FALSE(handler.has(0));
ASSERT_FALSE(handler.has(1));
ASSERT_EQ(handler.construct(0, 12), 12);
handler.reset();
ASSERT_TRUE(handler.empty());
ASSERT_EQ(handler.size(), 0u);
ASSERT_EQ(handler.begin(), handler.end());
ASSERT_FALSE(handler.has(0));
ASSERT_FALSE(handler.has(1));
}