EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
dfe_dispatcher.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file dfe_dispatcher.hpp
1 // SPDX-License-Identifier: MIT
2 // Copyright 2018 Moritz Kiehn
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 // SOFTWARE.
21 
26 
27 #pragma once
28 
29 #include <cassert>
30 #include <cstdint>
31 #include <functional>
32 #include <ostream>
33 #include <stdexcept>
34 #include <string>
35 #include <type_traits>
36 #include <unordered_map>
37 #include <utility>
38 #include <vector>
39 
40 namespace dfe {
41 
43 class Variable final {
44 public:
46  enum class Type { Empty, Boolean, Integer, Float, String };
47 
48  Variable() : m_type(Type::Empty) {}
49  Variable(Variable&& v) { *this = std::move(v); }
50  Variable(const Variable& v) { *this = v; }
51  explicit Variable(std::string&& s)
52  : m_string(std::move(s)), m_type(Type::String) {}
53  explicit Variable(const std::string& s) : Variable(std::string(s)) {}
54  explicit Variable(const char* s) : Variable(std::string(s)) {}
55  // suppport all possible integer types
57  explicit Variable(I integer)
58  : m_integer(static_cast<int64_t>(integer)), m_type(Type::Integer) {}
59  explicit Variable(double d) : m_float(d), m_type(Type::Float) {}
60  explicit Variable(float f) : Variable(static_cast<double>(f)) {}
61  explicit Variable(bool b) : m_boolean(b), m_type(Type::Boolean) {}
62  ~Variable() = default;
63 
65  Variable& operator=(const Variable& v);
66 
68  static Variable parse_as(const std::string& str, Type type);
69 
73  constexpr bool operator!() const { return m_type == Type::Empty; }
75  constexpr explicit operator bool() const { return !!(*this); }
77  constexpr Type type() const { return m_type; }
81  template<typename T>
82  auto as() const;
83 
84 private:
85  template<typename T>
86  struct Converter;
87  template<typename I>
89 
90  union {
91  int64_t m_integer;
92  double m_float;
93  bool m_boolean;
94  };
95  // std::string has non-trivial constructor; cannot store by value in union
96  // TODO 2018-11-29 msmk: more space-efficient string storage
97  std::string m_string;
99 
100  friend std::ostream& operator<<(std::ostream& os, const Variable& v);
101 };
102 
106 class Dispatcher {
107 public:
109  using Interface = std::function<Variable(const std::vector<Variable>&)>;
110 
117  void add(
118  std::string name, Interface&& func, std::vector<Variable::Type>&& arg_types,
119  std::string help = std::string());
123  template<typename R, typename... Args>
124  void add(
125  std::string name, std::function<R(Args...)>&& func,
126  std::string help = std::string());
127  template<typename R, typename... Args>
128  void add(
129  std::string name, R (*func)(Args...), std::string help = std::string());
130  template<typename T, typename R, typename... Args>
131  void add(
132  std::string name, R (T::*member_func)(Args...), T* t,
133  std::string help = std::string());
134 
136  template<typename... Args>
137  Variable call(const std::string& name, Args&&... args);
140  const std::string& name, const std::vector<std::string>& args);
143  const std::string& name, const std::vector<Variable>& args);
144 
146  std::vector<std::string> commands() const;
148  const std::string& help(const std::string& name) const;
149 
150 private:
151  struct Command {
153  std::vector<Variable::Type> argument_types;
154  std::string help;
155  };
156  std::unordered_map<std::string, Command> m_commands;
157 };
158 
159 // implementation Variable
160 
161 inline Variable
162 Variable::parse_as(const std::string& str, Type type) {
163  if (type == Type::Boolean) {
164  return Variable((str == "true"));
165  } else if (type == Type::Integer) {
166  return Variable(std::stoll(str));
167  } else if (type == Type::Float) {
168  return Variable(std::stod(str));
169  } else if (type == Type::String) {
170  return Variable(str);
171  } else {
172  return Variable();
173  }
174 }
175 
176 inline std::ostream&
177 operator<<(std::ostream& os, const Variable& v) {
178  if (v.type() == Variable::Type::Boolean) {
179  os << (v.m_boolean ? "true" : "false");
180  } else if (v.m_type == Variable::Type::Integer) {
181  os << v.m_integer;
182  } else if (v.m_type == Variable::Type::Float) {
183  os << v.m_float;
184  } else if (v.m_type == Variable::Type::String) {
185  os << v.m_string;
186  }
187  return os;
188 }
189 
190 inline Variable&
192  // handle `x = std::move(x)`
193  if (this == &v) {
194  return *this;
195  }
196  if (v.m_type == Type::Boolean) {
197  m_boolean = v.m_boolean;
198  } else if (v.m_type == Type::Integer) {
199  m_integer = v.m_integer;
200  } else if (v.m_type == Type::Float) {
201  m_float = v.m_float;
202  } else if (v.m_type == Type::String) {
203  m_string = std::move(v.m_string);
204  }
205  m_type = v.m_type;
206  return *this;
207 }
208 
209 inline Variable&
211  if (v.m_type == Type::Boolean) {
212  m_boolean = v.m_boolean;
213  } else if (v.m_type == Type::Integer) {
214  m_integer = v.m_integer;
215  } else if (v.m_type == Type::Float) {
216  m_float = v.m_float;
217  } else if (v.m_type == Type::String) {
218  m_string = v.m_string;
219  }
220  m_type = v.m_type;
221  return *this;
222 }
223 
224 template<>
225 struct Variable::Converter<bool> {
226  static constexpr Type type() { return Type::Boolean; }
227  static constexpr bool as_t(const Variable& v) { return v.m_boolean; }
228 };
229 template<>
230 struct Variable::Converter<float> {
231  static constexpr Type type() { return Type::Float; }
232  static constexpr float as_t(const Variable& v) {
233  return static_cast<float>(v.m_float);
234  }
235 };
236 template<>
237 struct Variable::Converter<double> {
238  static constexpr Type type() { return Type::Float; }
239  static constexpr double as_t(const Variable& v) { return v.m_float; }
240 };
241 template<>
242 struct Variable::Converter<std::string> {
243  static constexpr Type type() { return Type::String; }
244  static constexpr const std::string& as_t(const Variable& v) {
245  return v.m_string;
246  }
247 };
248 template<typename I>
250  static constexpr Type type() { return Type::Integer; }
251  static constexpr I as_t(const Variable& v) {
252  return static_cast<I>(v.m_integer);
253  }
254 };
255 template<>
256 struct Variable::Converter<int8_t> : Variable::IntegerConverter<int8_t> {};
257 template<>
258 struct Variable::Converter<int16_t> : Variable::IntegerConverter<int16_t> {};
259 template<>
260 struct Variable::Converter<int32_t> : Variable::IntegerConverter<int32_t> {};
261 template<>
262 struct Variable::Converter<int64_t> : Variable::IntegerConverter<int64_t> {};
263 template<>
264 struct Variable::Converter<uint8_t> : Variable::IntegerConverter<uint8_t> {};
265 template<>
266 struct Variable::Converter<uint16_t> : Variable::IntegerConverter<uint16_t> {};
267 template<>
268 struct Variable::Converter<uint32_t> : Variable::IntegerConverter<uint32_t> {};
269 template<>
270 struct Variable::Converter<uint64_t> : Variable::IntegerConverter<uint64_t> {};
271 
272 template<typename T>
273 inline auto
274 Variable::as() const {
275  if (m_type != Variable::Converter<T>::type()) {
276  throw std::invalid_argument(
277  "Requested type is incompatible with stored type");
278  }
279  return Variable::Converter<T>::as_t(*this);
280 }
281 
282 // implementation Dispatcher
283 
284 namespace dispatcher_impl {
285 namespace {
286 
287 // Wrap a function that returns a value
288 template<typename R, typename... Args>
289 struct InterfaceWrappper {
290  std::function<R(Args...)> func;
291 
292  Variable operator()(const std::vector<Variable>& args) {
293  return call(args, std::index_sequence_for<Args...>());
294  }
295  template<std::size_t... I>
296  Variable call(const std::vector<Variable>& args, std::index_sequence<I...>) {
297  return Variable(func(args.at(I).as<typename std::decay_t<Args>>()...));
298  }
299 };
300 
301 // Wrap a function that does not return anything
302 template<typename... Args>
303 struct InterfaceWrappper<void, Args...> {
304  std::function<void(Args...)> func;
305 
306  Variable operator()(const std::vector<Variable>& args) {
307  return call(args, std::index_sequence_for<Args...>());
308  }
309  template<std::size_t... I>
310  Variable call(const std::vector<Variable>& args, std::index_sequence<I...>) {
311  func(args.at(I).as<typename std::decay_t<Args>>()...);
312  return Variable();
313  }
314 };
315 
316 template<typename R, typename... Args>
318 make_wrapper(std::function<R(Args...)>&& function) {
319  return InterfaceWrappper<R, Args...>{std::move(function)};
320 }
321 
322 template<typename R, typename... Args>
323 std::vector<Variable::Type>
324 make_types(const std::function<R(Args...)>&) {
325  return {Variable(std::decay_t<Args>()).type()...};
326 }
327 
328 } // namespace
329 } // namespace dispatcher_impl
330 
331 inline void
333  std::string name, Dispatcher::Interface&& func,
334  std::vector<Variable::Type>&& arg_types, std::string help) {
335  if (name.empty()) {
336  throw std::invalid_argument("Can not register command with empty name");
337  }
338  if (m_commands.count(name)) {
339  throw std::invalid_argument(
340  "Can not register command '" + name + "' more than once");
341  }
342  m_commands[std::move(name)] =
343  Command{std::move(func), std::move(arg_types), std::move(help)};
344 }
345 
346 template<typename R, typename... Args>
347 inline void
349  std::string name, std::function<R(Args...)>&& func, std::string help) {
350  auto args = dispatcher_impl::make_types(func);
351  add(
352  std::move(name), dispatcher_impl::make_wrapper(std::move(func)),
353  std::move(args), std::move(help));
354 }
355 
356 template<typename R, typename... Args>
357 inline void
358 Dispatcher::add(std::string name, R (*func)(Args...), std::string help) {
359  assert(func && "Function pointer must be non-null");
360  add(std::move(name), std::function<R(Args...)>(func), std::move(help));
361 }
362 
363 template<typename T, typename R, typename... Args>
364 inline void
366  std::string name, R (T::*member_func)(Args...), T* t, std::string help) {
367  assert(member_func && "Member function pointer must be non-null");
368  assert(t && "Object pointer must be non-null");
369  add(
370  std::move(name), std::function<R(Args...)>([=](Args... args) {
371  return (t->*member_func)(args...);
372  }),
373  std::move(help));
374 }
375 
376 inline Variable
377 Dispatcher::call_native(
378  const std::string& name, const std::vector<Variable>& args) {
379  auto cmd = m_commands.find(name);
380  if (cmd == m_commands.end()) {
381  throw std::invalid_argument("Unknown command '" + name + "'");
382  }
383  if (args.size() != cmd->second.argument_types.size()) {
384  throw std::invalid_argument("Invalid number of arguments");
385  }
386  return cmd->second.func(args);
387 }
388 
389 inline Variable
390 Dispatcher::call_parsed(
391  const std::string& name, const std::vector<std::string>& args) {
392  // dont reuse call_native since we need to have access to the command anyways
393  auto cmd = m_commands.find(name);
394  if (cmd == m_commands.end()) {
395  throw std::invalid_argument("Unknown command '" + name + "'");
396  }
397  if (args.size() != cmd->second.argument_types.size()) {
398  throw std::invalid_argument("Invalid number of arguments");
399  }
400  // convert string arguments into Variable values
401  std::vector<Variable> vargs;
402  for (std::size_t i = 0; i < args.size(); ++i) {
403  vargs.push_back(Variable::parse_as(args[i], cmd->second.argument_types[i]));
404  }
405  return cmd->second.func(vargs);
406 }
407 
408 template<typename... Args>
409 inline Variable
410 Dispatcher::call(const std::string& name, Args&&... args) {
411  return call_native(
412  name, std::vector<Variable>{Variable(std::forward<Args>(args))...});
413 }
414 
415 inline std::vector<std::string>
416 Dispatcher::commands() const {
417  std::vector<std::string> cmds;
418 
419  for (const auto& cmd : m_commands) {
420  cmds.emplace_back(cmd.first);
421  }
422  return cmds;
423 }
424 
425 inline const std::string&
426 Dispatcher::help(const std::string& name) const {
427  return m_commands.at(name).help;
428 }
429 
430 } // namespace dfe