Skip to content

Instantly share code, notes, and snippets.

@alecthomas
Created October 14, 2015 22:40
Show Gist options
  • Save alecthomas/24d0f33fbfc9ec1e5736 to your computer and use it in GitHub Desktop.
Save alecthomas/24d0f33fbfc9ec1e5736 to your computer and use it in GitHub Desktop.
Experimental (not working) EntityX transactional support
#include <condition_variable>
#include <thread>
#include <mutex>
#include <iostream>
#include <algorithm>
#include <type_traits>
#include <vector>
#include <cstdint>
#include <iterator>
#include <functional>
#include <cassert>
#include <unordered_map>
#include <set>
#include <atomic>
using std::cout;
using std::endl;
template <typename T>
std::ostream &operator << (std::ostream &out, const std::vector<T> &v) {
out << "[";
for (auto &i : v) out << i << " ";
out << "]";
return out;
}
namespace details {
template <typename T, typename... Ts>
struct get_component_index;
template <typename T, typename... Ts>
struct get_component_index<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename Tail, typename... Ts>
struct get_component_index<T, Tail, Ts...> :
std::integral_constant<std::size_t, 1 + get_component_index<T, Ts...>::value> {};
template <typename T, typename... Ts>
struct get_component_size;
template <typename T, typename... Ts>
struct get_component_size<T, T, Ts...> : std::integral_constant<std::size_t, sizeof(T)> {};
template <typename T, typename Tail, typename... Ts>
struct get_component_size<T, Tail, Ts...> :
std::integral_constant<std::size_t, get_component_size<T, Ts...>::value> {};
template <typename T, typename... Ts>
struct is_valid_component;
template <typename T, typename... Ts>
struct is_valid_component<T, T, Ts...> : std::true_type {};
template <typename T, typename Tail, typename... Ts>
struct is_valid_component<T, Tail, Ts...> : is_valid_component<T, Ts...>::type {};
template <typename T>
struct is_valid_component<T> {
// condition is always false, but should be dependant of T
static_assert(sizeof(T) == 0, "type is not a component");
};
template <typename T, typename... Ts>
struct get_component_offset;
template <typename T, typename... Ts>
struct get_component_offset<T, T, Ts...> : std::integral_constant<std::size_t, 0> {};
template <typename T, typename Tail, typename... Ts>
struct get_component_offset<T, Tail, Ts...> :
std::integral_constant<std::size_t, sizeof(Tail) + get_component_offset<T, Ts...>::value> {};
template <typename... Ts>
struct get_component_size_sum;
template <typename T>
struct get_component_size_sum<T> : std::integral_constant<std::size_t, sizeof(T)> {};
template <typename T, typename... Ts>
struct get_component_size_sum<T, Ts...> :
std::integral_constant<std::size_t, sizeof(T) + get_component_size_sum<Ts...>::value> {};
// Apply a template method to each type in a variadic template:
//
// template <typename T> struct print {
// void apply(int) {
// cout << get<T>() << endl;
// }
// }
// each<print, 0, int, float>();
template <template <typename> class F, int N, typename T>
void each() {
F<T>::apply(N);
}
template <template <typename> class F, int N, typename T0, typename T1, typename ... Ts>
void each() {
each<F, N, T0>();
each<F, N + 1, T1, Ts...>();
}
} // namespace details
template <typename C>
class Component {
public:
std::uint32_t id() const { return it_->first; }
C *operator -> () { return &it_->second; }
operator C* () { return &it_->second; }
const C *operator -> () const { return &it_->second; }
operator const C* () const { return &it_->second; }
bool operator !() const { return it_ == end_; }
private:
template <typename ... Components>
friend class EntityManager;
template <typename ... Components>
friend class Storage;
explicit Component(const typename std::unordered_map<std::uint32_t, C>::iterator &it,
const typename std::unordered_map<std::uint32_t, C>::iterator &end) : it_(it), end_(end) {}
typename std::unordered_map<std::uint32_t, C>::iterator it_, end_;
};
template <typename ... Components>
class Storage {
public:
explicit Storage(std::atomic<std::uint32_t> &id) : id_(id) {
init<0, Components...>();
}
~Storage() { destroy<0, Components...>(); }
std::uint32_t create() {
return id_++;
}
template <typename C, typename ... Args>
Component<C> assign(std::uint32_t id, Args && ... args) {
auto &t = get<C>();
auto pair = t.emplace(id, C(std::forward<Args>(args)...));
return Component<C>(pair.first, t.end());
}
template <typename C>
Component<C> get(std::uint32_t index) {
auto &t = get<C>();
return Component<C>(t.find(index), t.end());
}
template <typename C>
void remove(std::uint32_t index) {
get<C>().erase(index);
}
void destroy(std::uint32_t index) {
destroy<Components...>(index);
}
private:
template <typename C>
using Map = std::unordered_map<std::uint32_t, C>;
template <typename ... Cs>
friend class Transaction;
template <typename C>
std::unordered_map<std::uint32_t, C> &get() {
return *reinterpret_cast<Map<C>*>(components_[details::get_component_index<C, Components...>::value]);
}
template <int N, typename C>
void init() {
components_[N] = new Map<C>();
}
template <int N, typename C0, typename C1, typename ... Cn>
void init() {
init<N, C0>();
init<N+1, C1, Cn...>();
}
template <int N, typename C>
void destroy() {
std::unordered_map<std::uint32_t, C> *m = components_[N];
delete m;
}
template <int N, typename C0, typename C1, typename ... Cn>
void destroy() {
init<N, C0>();
init<N+1, C1, Cn...>();
}
template <typename C>
void merge(Storage &storage) {
auto &dest = get<C>();
for (auto it : storage.get<C>())
dest[it.first] = it.second;
}
template <typename C0, typename C1, typename ... Cn>
void merge(Storage &storage) {
merge<C0>(storage);
merge<C1, Cn...>(storage);
}
void *components_[sizeof...(Components)];
std::atomic<std::uint32_t> &id_;
};
template <typename ... Components>
class EntityManager {
public:
EntityManager() : id_(0), storage_(id_) {}
std::uint32_t create() {
return storage_.create();
}
template <typename C, typename ... Args>
Component<C> assign(std::uint32_t id, Args && ... args) {
return storage_.template assign<C, Args...>(id, std::forward<Args>(args)...);
}
private:
template <typename ... Cs>
friend class TransactionManager;
std::atomic<std::uint32_t> id_;
Storage<Components...> storage_;
};
template <typename ... Components>
class Transaction {
public:
std::uint32_t create() {
return change_->tx.create();
}
template <typename C>
Component<C> get(std::uint32_t id) {
Component<C> c = change_->tx.template get<C>(id);
if (!c) {
c = parent_.template get<C>(id);
}
return c;
}
template <typename C, typename ... Args>
Component<C> assign(std::uint32_t id, Args && ... args) {
return change_->tx.template assign<C, Args...>(id, std::forward<Args>(args)...);
}
void destroy(std::uint32_t id) {
change_->deletes.push_back(id);
}
private:
template <typename ... Cs>
friend class TransactionManager;
explicit Transaction(Storage<Components...> &parent, typename Storage<Components...>::Change *change) : parent_(parent), change_(change) {}
Storage<Components...> &parent_;
typename Storage<Components...>::Change change_;
};
template <typename ... Components>
class TransactionManager {
public:
explicit TransactionManager(EntityManager<Components...> &em) : em_(em) {}
Transaction<Components...> begin() {
auto state = new typename Transaction<Components...>::State{
Storage<Components...>(em_.id_),
};
transactions_.push_back(state);
return Transaction<Components...>(em_.storage_, state);
}
void commit() {
for (auto &state : transactions_)
em_.storage_.merge(*state);
for (auto state : transactions_)
for (auto id : storage.)
}
private:
EntityManager<Components...> &em_;
std::vector<Storage<Components...>::State*> transactions_;
};
struct Direction {
Direction() = default;
Direction(float x, float y) : x(x), y(y) {}
float x, y;
};
std::ostream &operator << (std::ostream &out, const Direction &p) {
return out << "(" << p.x << ", " << p.y << ")";
}
struct Position {
Position() = default;
Position(float x, float y): x(x), y(y) {}
float x, y;
};
std::ostream &operator << (std::ostream &out, const Position &p) {
return out << "(" << p.x << ", " << p.y << ")";
}
template <typename ... Components>
TransactionManager<Components...>
int main() {
EntityManager<Position, Direction> entities;
{
TransactionManager<Position, Direction> transactions(entities);
// Thread 0
auto tx = transactions.begin();
std::uint32_t id = tx.create();
cout << *tx.assign<Position>(id, 1, 2.5) << endl;
cout << *tx.assign<Direction>(id, 3, 4) << endl;
tx.commit();
transactions.commit();
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment