diff --git a/source/main.cpp b/source/main.cpp
index 4d22cc05a393616df0bf2bc6f8e2c57368cd5dba..e9cfcd008182c3ef255c3825b838d96cb61b66bd 100644
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -12,8 +12,8 @@
#include "digit_display.h"
#include "timer.h"
-const std::chrono::steady_clock::duration max_duration = 99h + 59min + 59s;
-const std::chrono::steady_clock::duration min_duration{};
+// const std::chrono::steady_clock::duration max_duration = 99h + 59min + 59s;
+// const std::chrono::steady_clock::duration min_duration{};
struct nothing {};
using movement = motion::movement<std::chrono::steady_clock::duration, nothing, nothing>;
@@ -98,12 +98,12 @@ int main(int argc, const char** argv) try
auto current_timer = motion::symphony{support::make_range(timers)};
bool paused = true;
- ui_shop ui{ ui_factory<i_ui_object, i_graphic, i_interactive>{} };
+ entities ui{ components<object_interface<i_ui_object, i_graphic, i_interactive>>{} };
rect button_rect{win.size()/8 + int2{5,5}};
auto make_control_button = [&]() -> auto&
{
- auto& button = ui.make<plain_button>(fg_color, button_rect);
+ auto& button = ui.emplace<plain_button>(fg_color, button_rect);
return button;
};
auto& stop_button = make_control_button();
@@ -135,14 +135,14 @@ int main(int argc, const char** argv) try
auto make_timer_ui = [&ui, &fg_color, &focus_handler]()
{
auto [hours_display, minutes_display, seconds_display] =
- ui.make_order([&](auto& factory)
+ ui.make([&](auto& components)
{
int2 digit_size{40,100};
auto digit_spacing = int2::i(5);
auto make_time_display = [&]() -> auto&
{
// oof, template -_-
- auto& display = factory.template make<digit_display<>>(digit_size, digit_spacing, fg_color);
+ auto& display = components.template emplace<digit_display<>>(digit_size, digit_spacing, fg_color);
return display;
};
return std::tie
@@ -151,7 +151,7 @@ int main(int argc, const char** argv) try
make_time_display(),
make_time_display()
);
- }).goods;
+ }).components;
hours_display.on_press.push_back(focus_handler);
minutes_display.on_press.push_back(focus_handler);
@@ -161,9 +161,9 @@ int main(int argc, const char** argv) try
bounds_layout timer_layout(
{
&hours_display,
- &ui.make<digit_bitmap>(digit[10], fg_color, separator_rect),
+ &ui.emplace<digit_bitmap>(digit[10], fg_color, separator_rect),
&minutes_display,
- &ui.make<digit_bitmap>(digit[10], fg_color, separator_rect),
+ &ui.emplace<digit_bitmap>(digit[10], fg_color, separator_rect),
&seconds_display
},
int2::i(5)
@@ -274,14 +274,14 @@ int main(int argc, const char** argv) try
main_layout.elements.push_back(&layout);
main_layout.update();
- h.on_input.push_back([&timer, &h](auto&&, int old_value, int new_value)
+ h.on_input.push_back([&timer](auto&&, int old_value, int new_value)
{
using namespace std::chrono;
auto offset = hours(new_value) - hours(old_value);
timer.reset();
timer.total = timer.total - timer.elapsed + offset;
});
- m.on_input.push_back([&timer, &m](auto&&, int old_value, int new_value)
+ m.on_input.push_back([&timer](auto&&, int old_value, int new_value)
{
using namespace std::chrono;
auto new_minutes = minutes(new_value);
@@ -291,7 +291,7 @@ int main(int argc, const char** argv) try
timer.reset();
timer.total = timer.total - timer.elapsed + offset;
});
- s.on_input.push_back([&timer, &s](auto&&, int old_value, int new_value)
+ s.on_input.push_back([&timer](auto&&, int old_value, int new_value)
{
using namespace std::chrono;
auto new_seconds = seconds(new_value);
@@ -307,7 +307,7 @@ int main(int argc, const char** argv) try
[](auto) { }
}, *event);
- for(auto&& interactive : ui.get<i_interactive>())
+ for(auto&& interactive : ui.get<i_interactive*>())
interactive->update(*event);
}
@@ -331,7 +331,7 @@ int main(int argc, const char** argv) try
fill(win.surface(), bg_color);
- for(auto&& graphic : ui.get<i_graphic>())
+ for(auto&& graphic : ui.get<i_graphic*>())
graphic->draw(win.surface());
win.update();
diff --git a/source/ui_factory.hpp b/source/ui_factory.hpp
index 95aea9b4aa4e94312bebfde86f11dbb1790da41b..a74cf063f6c4e13b9ef3d3450bc3876cf24302bf 100644
--- a/source/ui_factory.hpp
+++ b/source/ui_factory.hpp
@@ -6,180 +6,281 @@
#include "simple/support/tuple_utils.hpp"
-//TODO; nothing UI here anymore... also not really a factory and not really a shop... rename all the things!
template <typename... Interfaces>
-class ui_shop;
+class entities;
+
+using simple::support::flatten_t;
+using simple::support::flatten_meta_operator;
+
+template <typename T, T Offset, T... Is>
+constexpr auto offset_sequence(std::integer_sequence<T,Is...>)
+{
+ return std::integer_sequence<T, (Is + Offset)...>{};
+}
+
+template <typename T, T begin, T size>
+constexpr auto make_integer_segment()
+{
+ return offset_sequence<T, begin>(std::make_integer_sequence<T, size>{});
+}
template <typename Base, typename... Interfaces>
-class pointer_interface;
-
-// TODO: is lists of components, so call components
-// specify types of components
-// component type is either a value type or pointer_interface
-// value type just created and stored in appropriate vector
-// pointer_interface handled like below
-template <typename Type, typename... Interfaces>
-class ui_factory
+// TODO: enable if is_abstract<Base> && has_virtual_destructor<Base>
+// and maybe is_abstract<Interfaces>, but not sure
+struct object_interface
+{
+ static constexpr size_t type_count = 1 + sizeof...(Interfaces);
+};
+
+// TODO: parameterise std::unique_ptr and std::vector templates?? do I need that within a project or between projects, cause in a latter case can get away with using declaration in a configuration header
+template <typename... Types>
+class components
{
public:
- using type = Type;
+ using types = std::tuple<Types...>;
template <typename Element, typename ...Args>
- Element& make(Args&&... args)
+ Element& emplace(Args&&... args)
{
- using namespace simple::support;
- auto& elements = tuple_car(interfaces);
- auto element = std::make_unique<Element>(std::forward<Args>(args)...);
- Element& raw = *element.get();
+ using simple::support::find_meta_t;
+ using simple::support::bind_meta;
+ using simple::support::tuple_car;
+ using simple::support::tuple_car_t;
+ using simple::support::tuple_tie_cdr;
+ using simple::support::tie_subtuple;
+ using simple::support::is_template_instance_v;
+
+ using found = find_meta_t<bind_meta<is_component, size_constant<0>, Element>, types>;
+ static_assert(found::value != std::tuple_size_v<types>, "Element type not found in known component list.");
+ if constexpr (is_template_instance_v<object_interface, typename found::type>)
+ {
+ auto object_vectors = tie_subtuple(vectors,
+ make_integer_segment<size_t,
+ typename found::functor::binding{},
+ found::type::type_count
+ >()
+ );
+
+ // get the tuple of the pointer interface and do this
+ auto& elements = tuple_car(object_vectors);
- add_interface(raw, tuple_tie_cdr(interfaces));
+ auto element = std::make_unique<Element>(std::forward<Args>(args)...);
+ Element& raw = *element.get();
- elements.push_back(std::move(element)); // assuming raw pointer will not change... that's safe right?
- return raw;
+ add_interface(raw, tuple_tie_cdr(object_vectors));
+
+ elements.emplace_back(std::move(element)); // assuming raw pointer will not change... that's safe right?
+ return raw;
+ }
+ else
+ {
+ return std::get<found::value>().emplace_back(std::forward<Args>(args)...);
+ }
}
// TODO: return a range
- template <typename Interface>
+ template <typename Component>
const auto& get() const noexcept
{
- using ptr_t = std::conditional_t<std::is_same_v<Interface, type>,
- std::unique_ptr<type>,
- Interface*>;
- return std::get<std::vector<ptr_t>>(interfaces);
+ using simple::support::find_v;
+ return std::get<find_v<Component, flat_types>>(vectors);
}
private:
- template <typename... T>
- using container = std::tuple< std::vector<T> ... >;
- template <typename... T>
- using container_ref = std::tuple< std::vector<T>& ... >;
+ template <typename T>
+ struct flatten_object_interface : flatten_meta_operator<T> {};
+ template <typename Base, typename... Interfaces>
+ struct flatten_object_interface<object_interface<Base, Interfaces...>>
+ {
+ using type = std::tuple<std::unique_ptr<Base>, Interfaces*...>;
+ };
- container<
- std::unique_ptr<type>,
- Interfaces* ...
- > interfaces;
+ template <size_t i> using size_constant = std::integral_constant<size_t, i>;
+ template <typename flat_index, typename T, typename Component>
+ struct is_component : std::is_same<T,Component>
+ {
+ using binding = size_constant<flat_index{} + !is_component::value>;
+ };
+
+ template <typename flat_index, typename T, typename Base, typename... Interfaces>
+ struct is_component<flat_index, T, object_interface<Base,Interfaces...>>
+ {
+ static constexpr bool value = std::is_base_of_v<Base, T>;
+ static constexpr size_t increment =
+ [](bool value)
+ {
+ return value
+ ? 0
+ : object_interface<Base,Interfaces...>::type_count
+ ;
+ }
+ (is_component::value);
+ using binding = size_constant<flat_index{} + increment>;
+ };
+
+ using flat_types = flatten_t<types,flatten_object_interface>;
+
+ template <typename Tuple, bool ref = false>
+ struct container { };
+
+ template <typename... Ts>
+ struct container<std::tuple<Ts...>, false>
+ {
+ using type = std::tuple<std::vector<Ts>...>;
+ };
+
+ template <typename... Ts>
+ struct container<std::tuple<Ts...>, true>
+ {
+ using type = std::tuple<std::vector<Ts>&...>;
+ };
+
+ template <typename Tuple>
+ using container_t = typename container<Tuple>::type;
+
+ template <typename... Ts>
+ using container_ref_t = typename container<std::tuple<Ts...>, true>::type;
template <typename El>
void add_interface(El&, std::tuple<>){}
template <typename El, typename In, typename... Rest>
- void add_interface(El& element, container_ref<In*, Rest...> interfaces)
+ // NOTE: the shorthand can't deduce the In :/
+ // void add_interface(El& element, container_ref_t<In*, Rest...> interfaces)
+ void add_interface(El& element, std::tuple<std::vector<In*>&, std::vector<Rest>&...> interfaces)
{
- using namespace simple::support;
+ using simple::support::tuple_car;
+ using simple::support::tuple_tie_cdr;
if constexpr (std::is_base_of_v<In, El>)
tuple_car(interfaces).push_back(&element);
add_interface(element,tuple_tie_cdr(interfaces));
}
- friend class ui_shop<Type, Interfaces...>;
-};
+ container_t<flat_types> vectors;
+ friend class entities<Types...>;
+};
-// TODO: groups components into entities, so call entities
-template <typename... Interfaces>
-class ui_shop
+template <typename... Components>
+class entities
{
public:
- // TODO: rename reciept to entity
- using receipt_id_t = unsigned;
+ using entity_id_t = unsigned;
- ui_shop(ui_factory<Interfaces...> supplier) :
- supplier(std::move(supplier))
+ entities(components<Components...> components) :
+ _components(std::move(components))
{
}
- // TODO: rename order to entity, rename goods to components
- template <typename Goods>
- struct order
+ template <typename T>
+ struct entity
{
public:
- Goods goods;
- const receipt_id_t receipt_id;
+ T components;
+ const entity_id_t entity_id;
private:
- order(Goods&& goods, const receipt_id_t& receipt_id) :
- goods(std::forward<Goods>(goods)),
- receipt_id(receipt_id)
+ entity(T&& components, const entity_id_t& entity_id) :
+ components(std::forward<T>(components)),
+ entity_id(entity_id)
{}
- friend class ui_shop;
+ friend class entities;
};
// TODO: getter of components based on entity ids
+ // TODO; eplace specific components to replace the make function below,
+ // component type1, args1
+ // component_type2, args2
+ // ...
+ // component_typeN, argsN
+ //
+ // maybe use a component helper template, so it looks like this:
+ // entities.emplace
+ // (
+ // component<Type1>(args1...),
+ // component<Type2>(args2...),
+ // component<Type3>(args3...),
+ // component<Type4>(args4...),
+ // );
+ // can also sfinae on this helper template.
+ //
+ // return a predictable entity object with references to all components and the id
+
template <typename Function>
- order<std::invoke_result_t<
- Function, ui_factory<Interfaces...>&>>
- make_order(Function&& function)
+ entity<std::invoke_result_t<
+ Function, components<Components...>&>>
+ make(Function&& function)
{
using simple::support::transform;
auto size = transform([](const auto& x) {return x.size(); },
- supplier.interfaces);
+ _components.vectors);
- auto&& goods = std::invoke(
+ auto&& product = std::invoke(
std::forward<Function>(function),
- supplier);
+ _components);
- receipt_ids.push_back(get_id());
+ entity_ids.push_back(get_id());
- transform([](auto& receipt_size, auto size, const auto& interface)
+ transform([](auto& entity_size, auto size, const auto& interface)
{
- receipt_size.push_back(interface.size() - size);
- }, receipt_sizes, size, supplier.interfaces);
+ entity_size.push_back(interface.size() - size);
+ }, entity_sizes, size, _components.vectors);
return
{
- std::forward<decltype(goods)>(goods),
- receipt_ids.back()
+ std::forward<decltype(product)>(product),
+ entity_ids.back()
};
};
template <typename Element, typename ...Args>
- Element& make(Args&&... args)
+ Element& emplace(Args&&... args)
{
- return make_order([&](auto& factory) -> auto&
+ return make([&](auto& components) -> auto&
{
return
- factory.template make<Element>
+ components.template emplace<Element>
(std::forward<Args>(args)...);
- }).goods;
+ }).components;
}
- bool recycle(receipt_id_t id)
+ bool erase(entity_id_t id)
{
auto found = lower_bound
(
- begin(receipt_ids),
- end(receipt_ids),
+ begin(entity_ids),
+ end(entity_ids),
id
);
- if(found != end(receipt_ids))
+ if(found != end(entity_ids))
{
using simple::support::transform;
- transform([&](auto& interfaces, auto& sizes)
+ transform([&](auto& components, auto& sizes)
{
- auto size_begin = begin(receipt_sizes);
+ auto size_begin = begin(entity_sizes);
auto found_size = size_begin +
- (found - begin(receipt_ids));
+ (found - begin(entity_ids));
- auto goods_begin = begin(interfaces) +
+ auto components_begin = begin(components) +
accumulate(size_begin, found_size, 0);
- interfaces.erase
+ components.erase
(
- goods_begin,
- goods_begin + *found_size
+ components_begin,
+ components_begin + *found_size
);
sizes.erase(found_size);
- }, supplier.interfaces, receipt_sizes);
+ }, _components.components, entity_sizes);
- receipt_ids.erase(found);
+ entity_ids.erase(found);
return true;
}
@@ -188,28 +289,29 @@ class ui_shop
template <typename Interface>
decltype(auto) get() const noexcept
- { return supplier.template get<Interface>(); }
+ { return _components.template get<Interface>(); }
private:
- using receipt_size_t = unsigned short;
+ using entity_size_t = unsigned short;
- receipt_id_t get_id()
+ entity_id_t get_id()
{
- if(empty(receipt_ids))
+ if(empty(entity_ids))
return 0;
- return receipt_ids.back() + 1; //TODO: what if we run out?
+ return entity_ids.back() + 1; //TODO: what if we run out?
// but it'll take like two hundred bajillion years D':
// yes, what will this program do after two hundred bajillion years of runtime?
// reuse ids? do I smell std::rotate? =)
}
- ui_factory<Interfaces...> supplier;
- std::vector<receipt_id_t> receipt_ids;
+ using components_t = components<Components...>;
+ components_t _components;
+ std::vector<entity_id_t> entity_ids;
std::array<
- std::vector<receipt_size_t>,
- sizeof...(Interfaces) + 1
- > receipt_sizes;
+ std::vector<entity_size_t>,
+ std::tuple_size_v<typename components_t::flat_types>
+ > entity_sizes;
};