NamedFactory.h
Go to the documentation of this file.
1 #include <vector>
2 #ifndef WIRECELL_NAMEDFACTORY_H
3 #define WIRECELL_NAMEDFACTORY_H
4 
8 #include "WireCellUtil/Type.h"
9 #include "WireCellUtil/String.h"
11 #include "WireCellUtil/Logging.h"
12 #include <unordered_map>
13 
14 
15 #include <iostream> // fixme: remove
16 #include <exception>
17 #include <string>
18 #include <set>
19 
20 namespace WireCell {
21 
22  struct FactoryException : virtual public Exception {};
23 
24  /** A templated factory of objects of type Type that associates a
25  * name to an object, returning a preexisting one if it exists. */
26  template <class Type>
28  public:
29  /// Remember the underlying type.
30  typedef Type type;
31 
32  /// The exposed pointer type.
33  typedef std::shared_ptr<Type> pointer_type;
34 
35  NamedFactory() : m_classname("") {}
36 
37  /// Return existing instance of given name or nullptr if not found.
39  auto it = m_objects.find(name);
40  if (it == m_objects.end()) {
41  return nullptr;
42  }
43  return it->second;
44  }
45 
46  /// Return an instance associated with the given name.
47  Interface::pointer create() { return create(""); }
49  auto it = m_objects.find(name);
50  if (it == m_objects.end()) {
51  pointer_type p(new Type);
52  m_objects[name] = p;
53  return p;
54  }
55  return it->second;
56  }
57 
58  virtual void set_classname(const std::string& name) { m_classname=name; }
59  virtual const std::string& classname() { return m_classname; }
60 
61  private:
62  std::unordered_map<std::string, pointer_type> m_objects;
64  };
65 
66 
67 
68  /** A registry of factories that produce instances which implement
69  * a given interface. */
70  template <class IType>
73  public:
74  typedef IType interface_type;
75  typedef std::shared_ptr<IType> interface_ptr;
77  typedef std::unordered_map<std::string, factory_ptr> factory_lookup;
78  typedef std::set<std::string> known_type_set;
79 
80  NamedFactoryRegistry() : l(Log::logger("factory")) {}
81  size_t hello(const std::string& classname) {
82  m_known_types.insert(classname);
83  return m_known_types.size();
84  }
85  known_type_set known_types() const { return m_known_types; }
86 
87  /// Register an existing factory by the "class" name of the instance it can create.
88  bool associate(const std::string& classname, factory_ptr factory) {
89  m_lookup[classname] = factory;
90  return true;
91  }
92 
93 
94  /// Look up an existing factory by the name of the "class" it can create.
95  factory_ptr lookup_factory(const std::string& classname) {
96  if (classname == "") {
97  l->error("no class name given for type \"{}\"",
98  demangle(typeid(IType).name()));
99  return nullptr;
100  }
101 
102  auto it = m_lookup.find(classname);
103  if (it != m_lookup.end()) {
104  return it->second;
105  }
106 
107  // cache miss, try plugin
108 
110 
111  std::string factory_maker = "make_" + classname + "_factory";
112  auto plugin = pm.find(factory_maker.c_str());
113  if (!plugin) {
114  l->error("no plugin for \"{}\"", classname);
115  return nullptr;
116  }
117 
118  typedef void* (*maker_function)();
119  maker_function mf;
120  if (!plugin->symbol(factory_maker.c_str(), mf)) {
121  l->error("no factory maker symbol for \"{}\"", classname);
122  return nullptr;
123  }
124 
125  void* fac_void_ptr = mf();
126 
127  if (!fac_void_ptr) {
128  l->error("no factory for \"{}\"", classname);
129  return nullptr;
130  }
131 
132  factory_ptr fptr = reinterpret_cast<factory_ptr>(fac_void_ptr);
133  m_lookup[classname] = fptr;
134  return fptr;
135  }
136 
137  /// Return instance of give type and optional instance name.
138  /// If create is true, create the instance if it does not
139  /// exist. If nullok is true return nullptr if it does not
140  /// exist else throw by default.
141  interface_ptr instance(const std::string& classname, const std::string& instname = "",
142  bool create=true, bool nullok = false) {
143  factory_ptr fac = lookup_factory(classname);
144  if (!fac) {
145  if (nullok) {
146  return nullptr;
147  }
148  l->error("no factory for class \"{}\" (instance \"{}\")", classname, instname);
149  std::cerr << "WireCell::NamedFactory: no factory for class \""
150  << classname << "\", (instance \"" << instname << "\"\n";
151 
152  THROW(FactoryException() << errmsg{"No factory for class " + classname});
153  }
155  std::string action = "";
156  if (create) {
157  iptr = fac->create(instname);
158  action = "create";
159  }
160  else {
161  iptr = fac->find(instname);
162  action = "find";
163  }
164  if (!iptr) {
165  if (nullok) {
166  return nullptr;
167  }
168  std::string msg = "NamedFactory: Failed to "+action+" instance \"" + instname;
169  msg += "\" of class \"" + classname + "\"";
170  l->error(msg);
171  THROW(FactoryException() << errmsg{msg});
172  }
173  interface_ptr uptype = std::dynamic_pointer_cast<interface_type>(iptr);
174  if (!uptype) {
175  if (nullok) {
176  return nullptr;
177  }
178  std::string msg = "NamedFactory: Failed to cast instance: " + instname;
179  msg += " of class " + classname;
180  msg += " c++ type: " + type(iptr);
181  msg += " to " + type(uptype);
182  l->error(msg);
183  THROW(FactoryException() << errmsg{msg});
184  }
185  return uptype;
186  }
187 
188  /// Return a collection of class names known to this factory
189  /// registry. Note: linked/plugged shared libraries do not
190  /// automatically register their factories.
191  std::vector<std::string> known_classes() {
192  std::vector<std::string> ret;
193  for (auto it : m_lookup) {
194  ret.push_back(it.first);
195  }
196  return ret;
197  }
198 
199 
200  private:
201  factory_lookup m_lookup;
202  known_type_set m_known_types;
203  };
204 
205  /// Singleton interface
206  namespace Factory {
207 
208  /// Associate a factory with the type it makes.
209  template<class IType>
210  bool associate(const std::string& classname, WireCell::INamedFactory* factory) {
213  bool ok = nfr.associate(classname, factory);
214  if (ok) { return ok; }
215  THROW(FactoryException() << errmsg{"Failed to associate class " + classname});
216  }
217 
218  /// Lookup up a factory for the given type
219  template<class IType>
223  WireCell::INamedFactory* ret = nfr.lookup_factory(classname);
224  if (ret) { return ret; }
225  THROW(FactoryException() << errmsg{"Failed to lookup factory for " + classname});
226  }
227 
228  /// Lookup an interface by a type and optional name. If
229  /// create is true, create instance if missing. if nullok is
230  /// true return nullptr if can not create. See also
231  /// find<IType>().
232  template<class IType>
233  std::shared_ptr<IType> lookup(const std::string& classname,
234  const std::string& instname="",
235  bool create=true, bool nullok=false) {
238  std::shared_ptr<IType> ret = nfr.instance(classname, instname, create, nullok);
239  if (ret) { return ret; }
240  if (nullok) { return nullptr; }
241  THROW(FactoryException() << errmsg{"Failed to lookup instance for " + classname + " " + instname});
242  }
243 
244  /// Return existing instance of given classname with instname
245  /// Throws on failure.
246  template<class IType>
247  std::shared_ptr<IType> find(const std::string& classname, const std::string& instname="") {
248  std::shared_ptr<IType> ret = lookup<IType>(classname, instname, false, false);
249  return ret;
250  }
251  /// As above but quietly returns nullptr on failure
252  template<class IType>
253  std::shared_ptr<IType> find_maybe(const std::string& classname, const std::string& instname="") {
254  std::shared_ptr<IType> ret = lookup<IType>(classname, instname, false, true);
255  return ret;
256  }
257 
258  /// Lookup an interface by a type:name pair.
259  template<class IType>
260  std::shared_ptr<IType> lookup_tn(const std::string& tn, bool create=true, bool nullok=false) {
261  if (tn.empty()) {
262  if (nullok) {
263  return nullptr;
264  }
265  THROW(FactoryException() << errmsg{"Empty type:name string"});
266  }
267 
268  std::string t, n;
269  std::tie(t,n) = String::parse_pair(tn);
270  return lookup<IType>(t, n, create, nullok);
271  }
272 
273  /// Like lookup_tn but with create false.
274  template<class IType>
275  std::shared_ptr<IType> find_tn(const std::string& tn) {
276  std::shared_ptr<IType> ret = lookup_tn<IType>(tn, false, false);
277  return ret;
278  }
279  /// Like find_tn but with nullok true
280  template<class IType>
281  std::shared_ptr<IType> find_maybe_tn(const std::string& tn) {
282  std::shared_ptr<IType> ret = lookup_tn<IType>(tn, false, true);
283  return ret;
284  }
285 
286  /// Return a vector of all known classes of given interface.
287  template<class IType>
288  std::vector<std::string> known_classes() {
291  return nfr.known_classes();
292  }
293  template<class IType>
294  std::vector<std::string> known_types() {
297  auto ktset = nfr.known_types();
298  std::vector<std::string> ret(ktset.begin(), ktset.end());
299  return ret;
300  }
301  }
302 
303 } // namespace WireCell
304 
305 
306 
307 
308 
309 
310 template<class Concrete, class... Interface>
312  static void* void_factory = nullptr;
313  if (! void_factory) {
314  void_factory = new WireCell::NamedFactory<Concrete>;
316  = reinterpret_cast< WireCell::NamedFactory< Concrete >* >(void_factory);
317  std::vector<bool> ret{WireCell::Factory::associate<Interface>(name, factory)...};
318  }
319  return void_factory;
320 }
321 
322 
323 template<class Concrete, class... Interface>
325 {
326  std::vector<size_t> ret{WireCell::Singleton< WireCell::NamedFactoryRegistry<Interface> >::Instance().hello(name)...};
327  return ret.size();
328 }
329 
330 
331 
332 #define WIRECELL_FACTORY(NAME, CONCRETE,...) \
333  static size_t hello_##NAME##_me = namedfactory_hello< CONCRETE , __VA_ARGS__ >(#NAME); \
334  extern "C" { void* make_##NAME##_factory() { \
335  return make_named_factory_factory< CONCRETE , __VA_ARGS__ >(#NAME); \
336  }}
337 
338 #endif
virtual const std::string & classname()
Access name of class this factory can make.
Definition: NamedFactory.h:59
static QCString name
Definition: declinfo.cpp:673
std::shared_ptr< IType > find_maybe_tn(const std::string &tn)
Like find_tn but with nullok true.
Definition: NamedFactory.h:281
bool associate(const std::string &classname, factory_ptr factory)
Register an existing factory by the "class" name of the instance it can create.
Definition: NamedFactory.h:88
std::shared_ptr< Type > pointer_type
The exposed pointer type.
Definition: NamedFactory.h:33
void * make_named_factory_factory(std::string name)
Definition: NamedFactory.h:311
void msg(const char *fmt,...)
Definition: message.cpp:107
WireCell::INamedFactory * lookup_factory(const std::string &classname)
Lookup up a factory for the given type.
Definition: NamedFactory.h:220
std::string string
Definition: nybbler.cc:12
Interface::pointer create(const std::string &name)
Create an instance by name.
Definition: NamedFactory.h:48
boost::error_info< struct tag_errmsg, std::string > errmsg
Definition: Exceptions.h:54
Type type
Remember the underlying type.
Definition: NamedFactory.h:30
factory_ptr lookup_factory(const std::string &classname)
Look up an existing factory by the name of the "class" it can create.
Definition: NamedFactory.h:95
std::shared_ptr< IType > find_maybe(const std::string &classname, const std::string &instname="")
As above but quietly returns nullptr on failure.
Definition: NamedFactory.h:253
virtual void set_classname(const std::string &name)
Set name of class this factory can make.
Definition: NamedFactory.h:58
WireCell::INamedFactory * factory_ptr
Definition: NamedFactory.h:76
std::shared_ptr< IType > lookup(const std::string &classname, const std::string &instname="", bool create=true, bool nullok=false)
Definition: NamedFactory.h:233
std::pair< std::string, std::string > parse_pair(const std::string &in, const std::string &delim=":")
Definition: String.cxx:15
std::string m_classname
Definition: NamedFactory.h:63
interface_ptr instance(const std::string &classname, const std::string &instname="", bool create=true, bool nullok=false)
Definition: NamedFactory.h:141
virtual Interface::pointer create(const std::string &name)=0
Create an instance by name.
static PluginManager & instance()
std::vector< std::string > known_classes()
Definition: NamedFactory.h:191
std::unordered_map< std::string, pointer_type > m_objects
Definition: NamedFactory.h:62
Configuration find(Configuration &lst, const std::string &dotpath, const T &val)
Return dictionary in given list if it value at dotpath matches.
Plugin * find(const std::string &symbol_name)
std::vector< std::string > known_types()
Definition: NamedFactory.h:294
std::shared_ptr< IType > lookup_tn(const std::string &tn, bool create=true, bool nullok=false)
Lookup an interface by a type:name pair.
Definition: NamedFactory.h:260
size_t namedfactory_hello(std::string name)
Definition: NamedFactory.h:324
std::string demangle(const std::string &name)
Definition: Type.cxx:6
std::shared_ptr< Interface > pointer
Definition: Interface.h:16
std::shared_ptr< IType > find_tn(const std::string &tn)
Like lookup_tn but with create false.
Definition: NamedFactory.h:275
#define THROW(e)
Definition: Exceptions.h:25
logptr_t logger(std::string name)
Definition: Logging.cxx:71
std::shared_ptr< spdlog::logger > logptr_t
Definition: Logging.h:24
Definition: Main.h:22
p
Definition: test.py:223
std::vector< std::string > known_classes()
Return a vector of all known classes of given interface.
Definition: NamedFactory.h:288
Interface::pointer find(const std::string &name)
Return existing instance of given name or nullptr if not found.
Definition: NamedFactory.h:38
Interface::pointer create()
Return an instance associated with the given name.
Definition: NamedFactory.h:47
std::string type(const T &t)
Definition: Type.h:20
size_t hello(const std::string &classname)
Definition: NamedFactory.h:81
The base wire cell exception.
Definition: Exceptions.h:31
std::shared_ptr< spdlog::logger > create(std::string logger_name, SinkArgs &&...sink_args)
Definition: spdlog.h:45
bool associate(const std::string &classname, WireCell::INamedFactory *factory)
Associate a factory with the type it makes.
Definition: NamedFactory.h:210
std::unordered_map< std::string, factory_ptr > factory_lookup
Definition: NamedFactory.h:77
std::shared_ptr< IType > interface_ptr
Definition: NamedFactory.h:75
Type
Type of JSON value.
Definition: rapidjson.h:618
std::size_t n
Definition: format.h:3399
virtual Interface::pointer find(const std::string &name)=0
Return existing instance or nullptr if not found.
std::set< std::string > known_type_set
Definition: NamedFactory.h:78
known_type_set known_types() const
Definition: NamedFactory.h:85