1 //  Copyright (c)      2016 Thomas Heller
2 //
3 //  Distributed under the Boost Software License, Version 1.0. (See accompanying
4 //  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #include <hpx/throw_exception.hpp>
7 #include <hpx/runtime/actions/detail/action_factory.hpp>
8 #include <hpx/util/assert.hpp>
9 
10 #include <cstdint>
11 #include <string>
12 #include <unordered_map>
13 #include <utility>
14 #include <vector>
15 
16 namespace hpx { namespace actions { namespace detail
17 {
action_registry()18     action_registry::action_registry()
19       : max_id_(0)
20     {}
21 
register_factory(std::string const & type_name,ctor_t ctor)22     void action_registry::register_factory(std::string const& type_name, ctor_t ctor)
23     {
24         HPX_ASSERT(ctor != nullptr);
25 
26         typename_to_ctor_.emplace(std::string(type_name), ctor);
27 
28         // populate cache
29         typename_to_id_t::const_iterator it = typename_to_id_.find(type_name);
30         if (it != typename_to_id_.end())
31             cache_id(it->second, ctor);
32     }
33 
register_typename(std::string const & type_name,std::uint32_t id)34     void action_registry::register_typename(
35         std::string const& type_name, std::uint32_t id)
36     {
37         HPX_ASSERT(id != invalid_id);
38 
39         std::pair<typename_to_id_t::iterator, bool> p =
40             typename_to_id_.emplace(type_name, id);
41 
42         if (!p.second)
43         {
44             HPX_THROW_EXCEPTION(invalid_status,
45                 "action_registry::register_typename",
46                 "failed to insert " + type_name +
47                 " into typename to id registry.");
48         }
49 
50         // populate cache
51         typename_to_ctor_t::const_iterator it =
52             typename_to_ctor_.find(type_name);
53         if (it != typename_to_ctor_.end())
54             cache_id(id, it->second);
55 
56         if (id > max_id_) max_id_ = id;
57     }
58 
59     // This makes sure that the registries are consistent.
fill_missing_typenames()60     void action_registry::fill_missing_typenames()
61     {
62         // Register all type-names and ssign missing ids
63         for (std::string const& str : get_unassigned_typenames())
64             register_typename(str, ++max_id_);
65 
66         // Go over all registered mappings from type-names to ids and
67         // fill in missing id to constructor mappings.
68         for (auto const& d: typename_to_id_)
69         {
70             typename_to_ctor_t::const_iterator it =
71                 typename_to_ctor_.find(d.first);
72             if (it != typename_to_ctor_.end())
73                 cache_id(d.second, it->second);
74         }
75 
76         // Go over all registered mappings from type-names to ctors and
77         // fill in missing id to constructor mappings.
78         for (auto const& d: typename_to_ctor_)
79         {
80             typename_to_id_t::const_iterator it =
81                 typename_to_id_.find(d.first);
82             HPX_ASSERT(it != typename_to_id_.end());
83             cache_id(it->second, d.second);
84         }
85     }
86 
try_get_id(std::string const & type_name) const87     std::uint32_t action_registry::try_get_id(std::string const& type_name) const
88     {
89         typename_to_id_t::const_iterator it =
90             typename_to_id_.find(type_name);
91         if (it == typename_to_id_.end())
92             return invalid_id;
93 
94         return it->second;
95     }
96 
get_unassigned_typenames() const97     std::vector<std::string> action_registry::get_unassigned_typenames() const
98     {
99         typedef typename_to_ctor_t::value_type value_type;
100 
101         std::vector<std::string> result;
102 
103         for (const value_type& v: typename_to_ctor_)
104             if (!typename_to_id_.count(v.first))
105                 result.push_back(v.first);
106 
107         return result;
108     }
109 
get_id(std::string const & type_name)110     std::uint32_t action_registry::get_id(std::string const& type_name)
111     {
112         std::uint32_t id = instance().try_get_id(type_name);
113 
114         if (id == invalid_id)
115         {
116             HPX_THROW_EXCEPTION(serialization_error,
117                 "action_registry::get_id",
118                 "Unknown typename: " + type_name + "\n" +
119                 instance().collect_registered_typenames());
120         }
121 
122         return id;
123     }
124 
create(std::uint32_t id,bool with_continuation,std::string const * name)125     base_action* action_registry::create(
126         std::uint32_t id, bool with_continuation, std::string const* name)
127     {
128         action_registry& this_ = instance();
129 
130         if (id >= this_.cache_.size())
131         {
132             std::string msg("Unknown type descriptor " + std::to_string(id));
133 #if defined(HPX_DEBUG)
134             if (name != nullptr)
135             {
136                 msg += ", for typename " + *name;
137             }
138             msg += this_.collect_registered_typenames();
139 #endif
140             HPX_THROW_EXCEPTION(serialization_error,
141                 "action_registry::create", msg);
142             return nullptr;
143         }
144 
145         ctor_t ctor = this_.cache_[id];
146         if (ctor == nullptr)
147         {
148             std::string msg("Unknown type descriptor " + std::to_string(id));
149 #if defined(HPX_DEBUG)
150             if (name != nullptr)
151             {
152                 msg += ", for typename " + *name;
153             }
154             msg += this_.collect_registered_typenames();
155 #endif
156             HPX_THROW_EXCEPTION(serialization_error,
157                 "action_registry::create", msg);
158             return nullptr;
159         }
160         return ctor(with_continuation);
161     }
162 
instance()163     action_registry& action_registry::instance()
164     {
165         static action_registry this_;
166         return this_;
167     }
168 
cache_id(std::uint32_t id,action_registry::ctor_t ctor)169     void action_registry::cache_id(std::uint32_t id, action_registry::ctor_t ctor)
170     {
171         if (id >= cache_.size())
172         {
173             cache_.resize(id + 1, nullptr);
174             cache_[id] = nullptr;
175             return;
176         }
177 
178         if (cache_[id] == nullptr)
179         {
180             cache_[id] = ctor;
181         }
182     }
183 
collect_registered_typenames()184     std::string action_registry::collect_registered_typenames()
185     {
186 #if defined(HPX_DEBUG)
187         std::string msg("\nknown constructors:\n");
188 
189         for (auto const& desc : typename_to_ctor_)
190         {
191             msg += desc.first + "\n";
192         }
193 
194         msg += "\nknown typenames:\n";
195         for (auto const& desc : typename_to_id_)
196         {
197             msg += desc.first + " (";
198             msg += std::to_string(desc.second) + ")\n";
199         }
200         return msg;
201 #else
202         return std::string();
203 #endif
204     }
205 
206     ///////////////////////////////////////////////////////////////////////////
get_action_id_from_name(char const * action_name)207     std::uint32_t get_action_id_from_name(char const* action_name)
208     {
209         using hpx::actions::detail::action_registry;
210         return action_registry::get_id(action_name);
211     }
212 }}}
213