Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-02-21 09:58:12

0001 // SPDX-License-Identifier: MIT
0002 // Copyright 2018 Moritz Kiehn
0003 //
0004 // Permission is hereby granted, free of charge, to any person obtaining a copy
0005 // of this software and associated documentation files (the "Software"), to deal
0006 // in the Software without restriction, including without limitation the rights
0007 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0008 // copies of the Software, and to permit persons to whom the Software is
0009 // furnished to do so, subject to the following conditions:
0010 //
0011 // The above copyright notice and this permission notice shall be included in
0012 // all copies or substantial portions of the Software.
0013 //
0014 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0015 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0016 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0017 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0018 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0019 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
0020 // SOFTWARE.
0021 
0022 /// \file
0023 /// \brief   Command dispatcher to register functions and call them by name
0024 /// \author  Moritz Kiehn <msmk@cern.ch>
0025 /// \date    2018-02-20
0026 
0027 #pragma once
0028 
0029 #include <cassert>
0030 #include <cstdint>
0031 #include <functional>
0032 #include <ostream>
0033 #include <stdexcept>
0034 #include <string>
0035 #include <type_traits>
0036 #include <unordered_map>
0037 #include <utility>
0038 #include <vector>
0039 
0040 namespace dfe {
0041 
0042 /// Variable-type value object a.k.a. a poor mans std::variant.
0043 class Variable final {
0044 public:
0045   /// Supported value types.
0046   enum class Type { Empty, Boolean, Integer, Float, String };
0047 
0048   Variable() : m_type(Type::Empty) {}
0049   Variable(Variable&& v) { *this = std::move(v); }
0050   Variable(const Variable& v) { *this = v; }
0051   explicit Variable(std::string&& s)
0052     : m_string(std::move(s)), m_type(Type::String) {}
0053   explicit Variable(const std::string& s) : Variable(std::string(s)) {}
0054   explicit Variable(const char* s) : Variable(std::string(s)) {}
0055   // suppport all possible integer types
0056   template<typename I, typename = std::enable_if_t<std::is_integral<I>::value>>
0057   explicit Variable(I integer)
0058     : m_integer(static_cast<int64_t>(integer)), m_type(Type::Integer) {}
0059   explicit Variable(double d) : m_float(d), m_type(Type::Float) {}
0060   explicit Variable(float f) : Variable(static_cast<double>(f)) {}
0061   explicit Variable(bool b) : m_boolean(b), m_type(Type::Boolean) {}
0062   ~Variable() = default;
0063 
0064   Variable& operator=(Variable&& v);
0065   Variable& operator=(const Variable& v);
0066 
0067   /// Parse a string into a value of the requested type.
0068   static Variable parse_as(const std::string& str, Type type);
0069 
0070   /// In a boolean context a variable is false if it does not contain a value.
0071   ///
0072   /// \warning This is not the value of the stored boolean.
0073   constexpr bool operator!() const { return m_type == Type::Empty; }
0074   /// \see operator!()
0075   constexpr explicit operator bool() const { return !!(*this); }
0076   /// The type of the currently stored value.
0077   constexpr Type type() const { return m_type; }
0078   /// Get value of the variable as a specific type.
0079   ///
0080   /// \exception std::invalid_argument if the requested type is incompatible
0081   template<typename T>
0082   auto as() const;
0083 
0084 private:
0085   template<typename T>
0086   struct Converter;
0087   template<typename I>
0088   struct IntegerConverter;
0089 
0090   union {
0091     int64_t m_integer;
0092     double m_float;
0093     bool m_boolean;
0094   };
0095   // std::string has non-trivial constructor; cannot store by value in union
0096   // TODO 2018-11-29 msmk: more space-efficient string storage
0097   std::string m_string;
0098   Type m_type;
0099 
0100   friend std::ostream& operator<<(std::ostream& os, const Variable& v);
0101 };
0102 
0103 /// A simple command dispatcher.
0104 ///
0105 /// You can register commands and call them by name.
0106 class Dispatcher {
0107 public:
0108   /// The native dispatcher function interface.
0109   using Interface = std::function<Variable(const std::vector<Variable>&)>;
0110 
0111   /// Register a native dispatcher function.
0112   ///
0113   /// \param name      Unique function name
0114   /// \param func      Function object
0115   /// \param arg_types Arguments types
0116   /// \param help      Optional help text
0117   void add(
0118     std::string name, Interface&& func, std::vector<Variable::Type>&& arg_types,
0119     std::string help = std::string());
0120   /// Register a function with arbitrary arguments.
0121   ///
0122   /// The return type and the argument types must be compatible with `Variable`.
0123   template<typename R, typename... Args>
0124   void add(
0125     std::string name, std::function<R(Args...)>&& func,
0126     std::string help = std::string());
0127   template<typename R, typename... Args>
0128   void add(
0129     std::string name, R (*func)(Args...), std::string help = std::string());
0130   template<typename T, typename R, typename... Args>
0131   void add(
0132     std::string name, R (T::*member_func)(Args...), T* t,
0133     std::string help = std::string());
0134 
0135   /// Call a command with arbitrary arguments.
0136   template<typename... Args>
0137   Variable call(const std::string& name, Args&&... args);
0138   /// Call a command with arguments parsed from strings into the expected types.
0139   Variable call_parsed(
0140     const std::string& name, const std::vector<std::string>& args);
0141   /// Call a command using the native argument encoding.
0142   Variable call_native(
0143     const std::string& name, const std::vector<Variable>& args);
0144 
0145   /// Return a list of registered commands.
0146   std::vector<std::string> commands() const;
0147   /// Return the help text for the command.
0148   const std::string& help(const std::string& name) const;
0149 
0150 private:
0151   struct Command {
0152     Interface func;
0153     std::vector<Variable::Type> argument_types;
0154     std::string help;
0155   };
0156   std::unordered_map<std::string, Command> m_commands;
0157 };
0158 
0159 // implementation Variable
0160 
0161 inline Variable
0162 Variable::parse_as(const std::string& str, Type type) {
0163   if (type == Type::Boolean) {
0164     return Variable((str == "true"));
0165   } else if (type == Type::Integer) {
0166     return Variable(std::stoll(str));
0167   } else if (type == Type::Float) {
0168     return Variable(std::stod(str));
0169   } else if (type == Type::String) {
0170     return Variable(str);
0171   } else {
0172     return Variable();
0173   }
0174 }
0175 
0176 inline std::ostream&
0177 operator<<(std::ostream& os, const Variable& v) {
0178   if (v.type() == Variable::Type::Boolean) {
0179     os << (v.m_boolean ? "true" : "false");
0180   } else if (v.m_type == Variable::Type::Integer) {
0181     os << v.m_integer;
0182   } else if (v.m_type == Variable::Type::Float) {
0183     os << v.m_float;
0184   } else if (v.m_type == Variable::Type::String) {
0185     os << v.m_string;
0186   }
0187   return os;
0188 }
0189 
0190 inline Variable&
0191 Variable::operator=(Variable&& v) {
0192   // handle `x = std::move(x)`
0193   if (this == &v) {
0194     return *this;
0195   }
0196   if (v.m_type == Type::Boolean) {
0197     m_boolean = v.m_boolean;
0198   } else if (v.m_type == Type::Integer) {
0199     m_integer = v.m_integer;
0200   } else if (v.m_type == Type::Float) {
0201     m_float = v.m_float;
0202   } else if (v.m_type == Type::String) {
0203     m_string = std::move(v.m_string);
0204   }
0205   m_type = v.m_type;
0206   return *this;
0207 }
0208 
0209 inline Variable&
0210 Variable::operator=(const Variable& v) {
0211   if (v.m_type == Type::Boolean) {
0212     m_boolean = v.m_boolean;
0213   } else if (v.m_type == Type::Integer) {
0214     m_integer = v.m_integer;
0215   } else if (v.m_type == Type::Float) {
0216     m_float = v.m_float;
0217   } else if (v.m_type == Type::String) {
0218     m_string = v.m_string;
0219   }
0220   m_type = v.m_type;
0221   return *this;
0222 }
0223 
0224 template<>
0225 struct Variable::Converter<bool> {
0226   static constexpr Type type() { return Type::Boolean; }
0227   static constexpr bool as_t(const Variable& v) { return v.m_boolean; }
0228 };
0229 template<>
0230 struct Variable::Converter<float> {
0231   static constexpr Type type() { return Type::Float; }
0232   static constexpr float as_t(const Variable& v) {
0233     return static_cast<float>(v.m_float);
0234   }
0235 };
0236 template<>
0237 struct Variable::Converter<double> {
0238   static constexpr Type type() { return Type::Float; }
0239   static constexpr double as_t(const Variable& v) { return v.m_float; }
0240 };
0241 template<>
0242 struct Variable::Converter<std::string> {
0243   static constexpr Type type() { return Type::String; }
0244   static constexpr const std::string& as_t(const Variable& v) {
0245     return v.m_string;
0246   }
0247 };
0248 template<typename I>
0249 struct Variable::IntegerConverter {
0250   static constexpr Type type() { return Type::Integer; }
0251   static constexpr I as_t(const Variable& v) {
0252     return static_cast<I>(v.m_integer);
0253   }
0254 };
0255 template<>
0256 struct Variable::Converter<int8_t> : Variable::IntegerConverter<int8_t> {};
0257 template<>
0258 struct Variable::Converter<int16_t> : Variable::IntegerConverter<int16_t> {};
0259 template<>
0260 struct Variable::Converter<int32_t> : Variable::IntegerConverter<int32_t> {};
0261 template<>
0262 struct Variable::Converter<int64_t> : Variable::IntegerConverter<int64_t> {};
0263 template<>
0264 struct Variable::Converter<uint8_t> : Variable::IntegerConverter<uint8_t> {};
0265 template<>
0266 struct Variable::Converter<uint16_t> : Variable::IntegerConverter<uint16_t> {};
0267 template<>
0268 struct Variable::Converter<uint32_t> : Variable::IntegerConverter<uint32_t> {};
0269 template<>
0270 struct Variable::Converter<uint64_t> : Variable::IntegerConverter<uint64_t> {};
0271 
0272 template<typename T>
0273 inline auto
0274 Variable::as() const {
0275   if (m_type != Variable::Converter<T>::type()) {
0276     throw std::invalid_argument(
0277       "Requested type is incompatible with stored type");
0278   }
0279   return Variable::Converter<T>::as_t(*this);
0280 }
0281 
0282 // implementation Dispatcher
0283 
0284 namespace dispatcher_impl {
0285 namespace {
0286 
0287 // Wrap a function that returns a value
0288 template<typename R, typename... Args>
0289 struct InterfaceWrappper {
0290   std::function<R(Args...)> func;
0291 
0292   Variable operator()(const std::vector<Variable>& args) {
0293     return call(args, std::index_sequence_for<Args...>());
0294   }
0295   template<std::size_t... I>
0296   Variable call(const std::vector<Variable>& args, std::index_sequence<I...>) {
0297     return Variable(func(args.at(I).as<typename std::decay_t<Args>>()...));
0298   }
0299 };
0300 
0301 // Wrap a function that does not return anything
0302 template<typename... Args>
0303 struct InterfaceWrappper<void, Args...> {
0304   std::function<void(Args...)> func;
0305 
0306   Variable operator()(const std::vector<Variable>& args) {
0307     return call(args, std::index_sequence_for<Args...>());
0308   }
0309   template<std::size_t... I>
0310   Variable call(const std::vector<Variable>& args, std::index_sequence<I...>) {
0311     func(args.at(I).as<typename std::decay_t<Args>>()...);
0312     return Variable();
0313   }
0314 };
0315 
0316 template<typename R, typename... Args>
0317 inline Dispatcher::Interface
0318 make_wrapper(std::function<R(Args...)>&& function) {
0319   return InterfaceWrappper<R, Args...>{std::move(function)};
0320 }
0321 
0322 template<typename R, typename... Args>
0323 std::vector<Variable::Type>
0324 make_types(const std::function<R(Args...)>&) {
0325   return {Variable(std::decay_t<Args>()).type()...};
0326 }
0327 
0328 } // namespace
0329 } // namespace dispatcher_impl
0330 
0331 inline void
0332 Dispatcher::add(
0333   std::string name, Dispatcher::Interface&& func,
0334   std::vector<Variable::Type>&& arg_types, std::string help) {
0335   if (name.empty()) {
0336     throw std::invalid_argument("Can not register command with empty name");
0337   }
0338   if (m_commands.count(name)) {
0339     throw std::invalid_argument(
0340       "Can not register command '" + name + "' more than once");
0341   }
0342   m_commands[std::move(name)] =
0343     Command{std::move(func), std::move(arg_types), std::move(help)};
0344 }
0345 
0346 template<typename R, typename... Args>
0347 inline void
0348 Dispatcher::add(
0349   std::string name, std::function<R(Args...)>&& func, std::string help) {
0350   auto args = dispatcher_impl::make_types(func);
0351   add(
0352     std::move(name), dispatcher_impl::make_wrapper(std::move(func)),
0353     std::move(args), std::move(help));
0354 }
0355 
0356 template<typename R, typename... Args>
0357 inline void
0358 Dispatcher::add(std::string name, R (*func)(Args...), std::string help) {
0359   assert(func && "Function pointer must be non-null");
0360   add(std::move(name), std::function<R(Args...)>(func), std::move(help));
0361 }
0362 
0363 template<typename T, typename R, typename... Args>
0364 inline void
0365 Dispatcher::add(
0366   std::string name, R (T::*member_func)(Args...), T* t, std::string help) {
0367   assert(member_func && "Member function pointer must be non-null");
0368   assert(t && "Object pointer must be non-null");
0369   add(
0370     std::move(name), std::function<R(Args...)>([=](Args... args) {
0371       return (t->*member_func)(args...);
0372     }),
0373     std::move(help));
0374 }
0375 
0376 inline Variable
0377 Dispatcher::call_native(
0378   const std::string& name, const std::vector<Variable>& args) {
0379   auto cmd = m_commands.find(name);
0380   if (cmd == m_commands.end()) {
0381     throw std::invalid_argument("Unknown command '" + name + "'");
0382   }
0383   if (args.size() != cmd->second.argument_types.size()) {
0384     throw std::invalid_argument("Invalid number of arguments");
0385   }
0386   return cmd->second.func(args);
0387 }
0388 
0389 inline Variable
0390 Dispatcher::call_parsed(
0391   const std::string& name, const std::vector<std::string>& args) {
0392   // dont reuse call_native since we need to have access to the command anyways
0393   auto cmd = m_commands.find(name);
0394   if (cmd == m_commands.end()) {
0395     throw std::invalid_argument("Unknown command '" + name + "'");
0396   }
0397   if (args.size() != cmd->second.argument_types.size()) {
0398     throw std::invalid_argument("Invalid number of arguments");
0399   }
0400   // convert string arguments into Variable values
0401   std::vector<Variable> vargs;
0402   for (std::size_t i = 0; i < args.size(); ++i) {
0403     vargs.push_back(Variable::parse_as(args[i], cmd->second.argument_types[i]));
0404   }
0405   return cmd->second.func(vargs);
0406 }
0407 
0408 template<typename... Args>
0409 inline Variable
0410 Dispatcher::call(const std::string& name, Args&&... args) {
0411   return call_native(
0412     name, std::vector<Variable>{Variable(std::forward<Args>(args))...});
0413 }
0414 
0415 inline std::vector<std::string>
0416 Dispatcher::commands() const {
0417   std::vector<std::string> cmds;
0418 
0419   for (const auto& cmd : m_commands) {
0420     cmds.emplace_back(cmd.first);
0421   }
0422   return cmds;
0423 }
0424 
0425 inline const std::string&
0426 Dispatcher::help(const std::string& name) const {
0427   return m_commands.at(name).help;
0428 }
0429 
0430 } // namespace dfe