Skip to content

Instantly share code, notes, and snippets.

@jemand2001
Last active September 16, 2023 23:16
Show Gist options
  • Select an option

  • Save jemand2001/9ca1665fa72e3d052188124a2a694ae4 to your computer and use it in GitHub Desktop.

Select an option

Save jemand2001/9ca1665fa72e3d052188124a2a694ae4 to your computer and use it in GitHub Desktop.
tree-like structures between classes through template inheritance
#include <iostream>
#include <memory>
#include <ranges>
#include <source_location>
#include <vector>
#include "traits.hpp"
class Scene;
class Entity;
class Component;
template <class Me, class Child, class... Children>
class HasChildren<Me, Child, Children...> {
static_assert(unique<Child, Children...>,
"this class can't work if you have multiple identical types "
"of children");
template <typename C>
using Vec = std::vector<std::shared_ptr<C>>;
std::tuple<Vec<Child>, Vec<Children>...> children;
public:
template <one_of<Child, Children...> C>
void add_child(std::shared_ptr<C> c) {
if constexpr (can_check_parent<Me, C>) {
if (static_cast<Me *>(this)->check_parent(c))
throw std::runtime_error("loop detected while adding child!");
}
auto &them = std::get<Vec<C>>(children);
if (std::ranges::find(them, c) == them.end())
them.push_back(c);
if constexpr (can_set_parent<Child, Me> &&
std::derived_from<Me, std::enable_shared_from_this<Me>>) {
c->set_parent(static_cast<Me *>(this)->shared_from_this());
}
}
template <one_of<Child, Children...> C>
std::shared_ptr<C> _child(size_t x) {
return std::get<Vec<C>>(children).at(x);
}
template <one_of<Child, Children...> C>
std::shared_ptr<C> child(size_t x) {
return _child<C>(x);
}
std::shared_ptr<Child> child(size_t x) {
static_assert(sizeof...(Children) == 0,
"ambiguous call to "
"child()!");
return _child<Child>(x);
}
template <one_of<Child, Children...> C>
bool has_child(std::shared_ptr<C> c) {
Vec<C> &that = std::get<Vec<C>>(children);
return std::ranges::find(that, c) != that.end();
}
template <one_of<Child, Children...> C>
void remove_child(std::shared_ptr<C> c) {
children.remove(c);
}
template <one_of<Child, Children...> C>
std::ranges::ref_view<Vec<C>> _children_of() {
return {std::get<Vec<C>>(children)};
}
};
template <class Me>
struct HasChildren<Me> {
static_assert(
false,
"why are you inheriting HasChildren without any child types?");
};
template <typename Me, typename Parent, typename... Parents>
class HasParent<Me, Parent, Parents...> {
static_assert(unique<Parent, Parents...>,
"this class can't work if you have multiple identical types "
"of parents");
std::tuple<std::weak_ptr<Parent>, std::weak_ptr<Parents>...> parents;
public:
template <typename P>
bool check_parent(std::shared_ptr<P> potential) {
if constexpr (std::same_as<P, Me>)
if (potential.get() == static_cast<Me *>(this))
return true;
auto check = [potential]<typename P_>(std::weak_ptr<P_> parent) {
auto parent_ = parent.lock();
if constexpr (can_check_parent<P_, P>) {
return parent_ && parent_->check_parent(potential);
}
if constexpr (std::same_as<P, P_>)
return parent_ == potential;
return false;
};
return (check(std::get<std::weak_ptr<Parent>>(parents)) || ... ||
check(std::get<std::weak_ptr<Parents>>(parents)));
}
template <one_of<Parent, Parents...> P>
void set_parent(std::shared_ptr<P> p) {
if (p != std::get<std::weak_ptr<P>>(parents).lock())
std::get<std::weak_ptr<P>>(parents) = p;
}
template <one_of<Parent, Parents...> P>
std::shared_ptr<P> _try_get_parent() {
return std::get<std::weak_ptr<P>>(parents).lock();
}
template <one_of<Parent, Parents...> P>
std::shared_ptr<P> try_get_parent() {
return _try_get_parent<P>();
}
std::shared_ptr<Parent> try_get_parent() {
static_assert(sizeof...(Parents) == 0,
"ambiguous call to "
"try_get_parent()!");
return _try_get_parent<Parent>();
}
};
template <class Me>
struct HasParent<Me> {
static_assert(false,
"why are you inheriting HasParent without any parent types?");
};
class Scene : public std::enable_shared_from_this<Scene>,
public HasChildren<Scene, Entity> {};
class Entity : public std::enable_shared_from_this<Entity>,
public HasParent<Entity, Entity, Scene>,
public HasChildren<Entity, Entity, Component> {};
class Component : public std::enable_shared_from_this<Component>,
public HasParent<Component, Entity> {};
// class Wrong : public HasParent<Wrong, int, float, int> {};
int main() {
auto s = std::make_shared<Scene>();
s->add_child(std::make_shared<Entity>());
s->child(0)->add_child(std::make_shared<Entity>());
s->child(0)->add_child(std::make_shared<Component>());
std::cout << "root: " << s << '\n';
std::cout << s->child(0) << '\n';
std::cout << s->child(0)->try_get_parent<Scene>() << '\n';
std::cout << s->child(0)->child<Entity>(0) << '\n';
std::cout << s->child(0)->child<Entity>(0)->try_get_parent<Entity>()
<< '\n';
std::cout << s->child(0)->child<Component>(0) << '\n';
std::cout << s->child(0)->child<Component>(0)->try_get_parent() << '\n';
std::cout << std::boolalpha << s->has_child(s->child(0)) << '\n';
auto e1 = std::make_shared<Entity>();
auto e2 = std::make_shared<Entity>();
e1->add_child(e2);
try {
e2->add_child(e1); // throws
} catch (std::runtime_error &err) {
std::cout << "hell yeah, it threw: " << err.what() << '\n';
}
}
#include <memory>
#include <type_traits>
template <typename T, typename... Ts>
struct is_one_of;
template <typename T, typename... Ts>
struct is_one_of<T, T, Ts...> : std::true_type {};
template <typename T>
struct is_one_of<T> : std::false_type {};
template <typename T, typename R, typename... Ts>
struct is_one_of<T, R, Ts...> : is_one_of<T, Ts...> {};
template <typename T, typename... Ts>
concept one_of = is_one_of<T, Ts...>::value;
template <typename Me, typename... Parents>
class HasParent;
template <typename Me, typename... Children>
class HasChildren;
template <class Child, class Parent>
concept can_set_parent = requires(Child c, std::shared_ptr<Parent> p) {
{ c.set_parent(p) };
};
template <class Child, class Parent>
concept can_check_parent = requires(Child c, std::shared_ptr<Parent> p) {
{ c.check_parent(p) } -> std::same_as<bool>;
};
template <typename...>
struct is_unique;
template <typename T>
struct is_unique<T> : std::true_type {};
template <typename T, typename... Ts>
struct is_unique<T, Ts...> {
constexpr static bool value =
(!std::same_as<T, Ts> && ... && is_unique<Ts...>::value);
};
template <typename... Ts>
concept unique = is_unique<Ts...>::value;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment