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; };