PluginFactory.h
1 /*
2  * MoMEMta: a modular implementation of the Matrix Element Method
3  * Copyright (C) 2016 Universite catholique de Louvain (UCL), Belgium
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 
20 #pragma once
21 
22 #include <memory>
23 #include <string>
24 #include <unordered_map>
25 #include <vector>
26 
27 #include <momemta/Logging.h>
28 
29 // Plugin system
30 template<typename T> class PluginFactory;
31 
32 template<typename Interface, typename... Args>
33 class PluginFactory<Interface* (Args...)> {
34  public:
35  using type = Interface* (Args...);
36  using Factory = PluginFactory<Interface*(Args...)>;
37 
38  struct PMakerBase {
39  virtual std::shared_ptr<Interface> create(Args...) const = 0;
40  virtual ~PMakerBase() {}
41  };
42 
43  template<class PluginType>
44  struct PMaker: public PMakerBase {
45  PMaker(const std::string& name) {
46  Factory::get().registerPMaker(this, name);
47  }
48 
49  virtual std::shared_ptr<Interface> create(Args... args) const override {
50  return std::shared_ptr<PluginType>(new PluginType(std::forward<Args>(args)...));
51  }
52  };
53 
54  std::shared_ptr<Interface> create(const std::string& name, Args... args) const {
55  return find(name)->create(std::forward<Args>(args)...);
56  }
57 
58  static PluginFactory<Interface* (Args...)>& get();
59 
60  void registerPMaker(PMakerBase* pMaker, const std::string& name) {
61  auto it = m_plugins.find(name);
62  if (it != m_plugins.end())
63  throw plugin_already_exists_error("The plugin type '" + name + "' is already registered in the factory.");
64 
65  m_plugins.emplace(name, pMaker);
66  }
67 
68  PMakerBase* find(const std::string& name) const {
69  auto it = m_plugins.find(name);
70  if (it == m_plugins.end()) {
71  LOG(fatal) << "No such plugin type '" << name << "' registered in the factory (" << m_plugins.size() <<" plugins).";
72  throw plugin_not_found_error("No such plugin type '" + name + "' registered in the factory.");
73  }
74 
75  return it->second;
76  }
77 
78  std::vector<std::string> getPluginsList() const {
79  std::vector<std::string> result;
80  for (const auto& plugin: m_plugins) {
81  result.push_back(plugin.first);
82  }
83 
84  return result;
85  }
86 
87  private:
88  class plugin_already_exists_error: public std::runtime_error {
89  using std::runtime_error::runtime_error;
90  };
91 
92  class plugin_not_found_error: public std::runtime_error {
93  using std::runtime_error::runtime_error;
94  };
95 
96  PluginFactory() = default;
97 
98  PluginFactory(const PluginFactory&) = delete; // stop default
99  const PluginFactory& operator=(const PluginFactory&) = delete; // stop default
100 
101  std::unordered_map<std::string, PMakerBase*> m_plugins;
102 };
103 
104 #define PLUGIN_UNIQUE_NAME2(x, y) x ## y
105 #define PLUGIN_UNIQUE_NAME(x, y) PLUGIN_UNIQUE_NAME2(x, y)