Sequoia
Loading...
Searching...
No Matches
Factory.hpp
Go to the documentation of this file.
1
2// Copyright Oliver J. Rosten 2021. //
3// Distributed under the GNU GENERAL PUBLIC LICENSE, Version 3.0. //
4// (See accompanying file LICENSE.md or copy at //
5// https://www.gnu.org/licenses/gpl-3.0.en.html) //
7
8#pragma once
9
19
20#include <variant>
21#include <array>
22#include <tuple>
23#include <stdexcept>
24#include <algorithm>
25#include <string>
26
27namespace sequoia::object
28{
30 template<std::input_or_output_iterator Iterator>
32 {
33 public:
34 using value_type = typename std::iterator_traits<Iterator>::value_type;
35 using reference = std::string;
36
37 constexpr factory_dereference_policy() = default;
39
40 [[nodiscard]]
41 friend constexpr bool operator==(const factory_dereference_policy&, const factory_dereference_policy&) noexcept = default;
42
43 [[nodiscard]]
44 constexpr static reference get(Iterator i)
45 {
46 return i->first;
47 }
48 protected:
49 constexpr factory_dereference_policy(factory_dereference_policy&&) noexcept = default;
50
52
53 constexpr factory_dereference_policy& operator=(const factory_dereference_policy&) = default;
54 constexpr factory_dereference_policy& operator=(factory_dereference_policy&&) noexcept = default;
55 };
56
65 template<class... Products>
66 requires (sizeof...(Products) > 0) && (std::movable<Products> && ...)
67 class factory
68 {
69 public:
70 using key = std::string;
71 using vessel = std::variant<Products...>;
72
73 [[nodiscard]]
74 constexpr static std::size_t size() noexcept
75 {
76 return sizeof...(Products);
77 }
78 private:
79
80 template<class Product>
82
83 using creator_variant = std::variant<product_creator<Products>...>;
84 using element = std::pair<key, creator_variant>;
85 using storage = std::array<element, size()>;
86 using const_storage_iterator = typename storage::const_iterator;
87 public:
89
90 factory()
91 requires (has_extrinsic_nomenclator<Products> && ...)
93 {}
94
95 template<class... Names>
96 requires (sizeof...(Names) == size()) && (std::is_constructible_v<std::string, Names> && ...)
97 factory(Names... names)
98 : m_Creators{make_element<Products>(std::move(names))...}
99 {
100 std::ranges::sort(m_Creators, [](const element& lhs, const element& rhs){ return lhs.first < rhs.first; });
101
102 auto comp{[](const element& lhs, const element& rhs) { return lhs.first == rhs.first; }};
103 if(std::ranges::adjacent_find(m_Creators, comp) != m_Creators.cend())
104 throw std::logic_error{"Factory product names must be unique!"};
105 }
106
107 template<class... Args>
108 requires (initializable_from<Products, Args...> && ...)
109 [[nodiscard]]
110 vessel make(std::string_view name, Args&&... args) const
111 {
112 const auto found{find(name)};
113
114 if(found == m_Creators.end())
115 throw std::runtime_error{std::string{"Factory unable to make product of name '"}.append(name).append("'")};
116
117 return std::visit(overloaded{[&](const auto& v) { return vessel{v.make(std::forward<Args>(args)...)}; }}, found->second);
118 }
119
120 template<class Product, class... Args>
121 requires ( (std::is_same_v<Product, Products> || ...)
122 && (initializable_from<Products, Args...> && ...))
123 [[nodiscard]]
124 vessel make_or(std::string_view name, Args&&... args) const
125 {
126 auto found{find(name)};
127 if(found == m_Creators.end())
128 {
129 found = std::ranges::find_if(m_Creators, [](const element& e){ return std::holds_alternative<product_creator<Product>>(e.second); });
130 }
131
132 return std::visit(overloaded{[&](const auto& v) { return vessel{v.make(std::forward<Args>(args)...)}; }}, found->second);
133 }
134
135 [[nodiscard]]
136 friend bool operator==(const factory&, const factory&) noexcept = default;
137
138 [[nodiscard]]
139 names_iterator begin_names() const noexcept
140 {
141 return names_iterator{m_Creators.begin()};
142 }
143
144 [[nodiscard]]
145 names_iterator end_names() const noexcept
146 {
147 return names_iterator{m_Creators.end()};
148 }
149 private:
150 storage m_Creators{};
151
152 [[nodiscard]]
153 auto find(std::string_view name) const
154 {
155 const auto found{std::ranges::lower_bound(m_Creators, name, std::ranges::less{}, [](const element& e){ return e.first; })};
156
157 return (found != m_Creators.end()) && (found->first == name) ? found : m_Creators.end();
158 }
159
160 template<class Product>
161 [[nodiscard]]
162 static element make_element(std::string name)
163 {
164 if(name.empty())
165 throw std::logic_error{"Factory product names must not be empty!"};
166
167 return {std::move(name), product_creator<Product>{}};
168 }
169 };
170}
171
Concepts which are sufficiently general to appear in the sequoia namespace.
Traits, Concepts and basic utilities for the creation of objects.
Implementation for an iterator with policies controlling dereferencing and auxiliary data.
Utilities for associating types with names.
Meta-programming utilities.
Policy to allow iteration over the names of factory products.
Definition: Factory.hpp:32
Generic factory with statically defined products.
Definition: Factory.hpp:68
Creates a product for T.
Definition: Creator.hpp:74
An iterator with policies controlling dereferencing and auxiliary data.
Definition: Iterator.hpp:234
A concept similar to std::constructible_from, but which considers braced-init.
Definition: Concepts.hpp:75
Definition: Nomenclator.hpp:31
Definition: Utilities.hpp:73