WIP: improvements and new features
This commit is contained in:
@@ -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).
|
||||
-->
|
||||
|
||||
@@ -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
|
||||
209
src/registry.hpp
209
src/registry.hpp
@@ -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
205
src/sparse_set.hpp
Normal 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
97
src/tag_handler.hpp
Normal 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
|
||||
@@ -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})
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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>());
|
||||
}
|
||||
@@ -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
90
test/sparse_set.cpp
Normal 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
46
test/tag_handler.cpp
Normal 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));
|
||||
}
|
||||
Reference in New Issue
Block a user