Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2024-06-17 07:05:50

0001 // SPDX-License-Identifier: LGPL-3.0-or-later
0002 // Copyright (C) 2022 Wouter Deconinck, Sylvester Joosten
0003 //
0004 // Algorithm base class, defined as a template with a tuple of Input<> and Output<>
0005 // parameters.
0006 //
0007 // Known types are:
0008 //   - Normal data type T
0009 //   - Optional data type std::optional<T>
0010 //   - Vector of normal data std::vector<T>
0011 //
0012 // For input data, this then selects:
0013 //   - T           --> gsl::not_null<const T*> (NOT allowed to be null)
0014 //   - optional<T> --> const T* (allowed to be null)
0015 //   - vector<T>   --> std::vector<gsl::not_null<const T*>> (N arguments, NOT allowed to
0016 //                                                           be null, but can be zero
0017 //                                                           length)
0018 //
0019 // Same for output data, but replace `const T*` with `T*` (mutable) everywhere.
0020 //
0021 // The ::process() algorithm is then provided with a tuple of both the input and the
0022 // output pointers according to this scheme.
0023 //
0024 // Finally, provides provides utility traits to determine if a type Input<T...> or Output<T...> are
0025 // an Input or Output Type (is_input_v<U> and is_output_v<U>)
0026 //
0027 #pragma once
0028 
0029 #include <array>
0030 #include <map>
0031 #include <mutex>
0032 #include <optional>
0033 #include <string>
0034 #include <tuple>
0035 #include <typeindex>
0036 #include <typeinfo>
0037 #include <vector>
0038 
0039 #include <algorithms/detail/demangle.h>
0040 #include <algorithms/logger.h>
0041 #include <algorithms/name.h>
0042 #include <algorithms/property.h>
0043 #include <algorithms/service.h>
0044 #include <algorithms/type_traits.h>
0045 
0046 namespace algorithms {
0047 
0048 // T should either be the desired input type, a std::vector<> of the desired input type,
0049 // or a std::optional<> of the desired input type
0050 template <class... T> struct Input : std::tuple<input_type_t<T>...> {
0051   constexpr static const size_t kSize = sizeof...(T);
0052   using value_type                    = std::tuple<input_type_t<T>...>;
0053   using data_type                     = std::tuple<T...>;
0054   using key_type                      = std::array<const std::string, kSize>;
0055 };
0056 template <class... T> struct Output : std::tuple<output_type_t<T>...> {
0057   constexpr static const size_t kSize = sizeof...(T);
0058   using value_type                    = std::tuple<output_type_t<T>...>;
0059   using data_type                     = std::tuple<T...>;
0060   using key_type                      = std::array<const std::string, kSize>;
0061 };
0062 
0063 class AlgorithmBase : public PropertyMixin, public LoggerMixin, public NameMixin {
0064 public:
0065   AlgorithmBase(std::string_view name, std::string_view description)
0066       : LoggerMixin(name), NameMixin(name, description) {}
0067 };
0068 
0069 // TODO: C++20 Concepts version for better error handling
0070 template <class InputType, class OutputType> class Algorithm : public AlgorithmBase {
0071 public:
0072   using input_type     = InputType;
0073   using output_type    = OutputType;
0074   using algorithm_type = Algorithm;
0075   using Input          = typename input_type::value_type;
0076   using Output         = typename output_type::value_type;
0077   using InputNames     = typename input_type::key_type;
0078   using OutputNames    = typename output_type::key_type;
0079 
0080   Algorithm(std::string_view name, const InputNames& input_names, const OutputNames& output_names,
0081             std::string_view description)
0082       : AlgorithmBase(name, description)
0083       , m_input_names{input_names}
0084       , m_output_names{output_names} {}
0085 
0086   virtual ~Algorithm() {}
0087   virtual void init() {}
0088   virtual void process(const Input&, const Output&) const {}
0089 
0090   const InputNames& inputNames() const { return m_input_names; }
0091   const OutputNames& outputNames() const { return m_output_names; }
0092 
0093 private:
0094   const InputNames m_input_names;
0095   const OutputNames m_output_names;
0096 };
0097 
0098 // Algorithm service that stores factories for different algorithms indexed by their
0099 // underlying algorithm_type. This allows a framework to only need to know about every
0100 // algorithm_type (algorithm signature) once, which is sufficient to constrain all
0101 // compile-time code needed.
0102 class AlgorithmSvc : public LoggedService<AlgorithmSvc> {
0103 public:
0104   // Add a new factory for an algorithm with a specified type. The full demangled type name
0105   // will be used as identifier for the factory
0106   template <class Algo> void add() {
0107     static std::mutex m;
0108     static std::lock_guard<std::mutex> lock{m};
0109     auto type            = Algo::algorithm_type;
0110     std::type_index name = detail::demangledName<Algo>();
0111     if (!available<type>()) {
0112       m_factories[type] = {};
0113     }
0114     if (available<type>(name)) {
0115       return; // do nothing, already there
0116     }
0117     m_factories[type].emplace({name, []() { return static_cast<AlgorithmBase*>(new Algo()); }});
0118   }
0119 
0120   // Get a new owning pointer to an instance of an algorithm.
0121   // Throws if the resource isn't available, or if we aren't fully ready yet.
0122   template <class AlgoType> std::unique_ptr<AlgoType> get(std::string_view name) const {
0123     ensureReady();
0124     if (!available<AlgoType>(name)) {
0125       raise(fmt::format("No factory with name {} and type {} registered with the AlgorithmSvc",
0126                         name, typeid(AlgoType).name()));
0127     }
0128     const auto& factories = m_factories.at(typeid(AlgoType));
0129     // This creates and object and gives ownership to the unique_ptr
0130     return static_cast<AlgoType*>(factories.at(name)());
0131   }
0132   // Return a vector of the names of all available algorithms of a certain type
0133   // Just return an empty vector if no names are present (don't throw any exceptions
0134   // for missing entries here).
0135   // Throws if we aren't fully ready yet.
0136   template <class AlgoType> auto ls() const {
0137     ensureReady();
0138     std::vector<std::string_view> ret;
0139     if (available<AlgoType>()) {
0140       std::for_each(factory<AlgoType>().begin(), factory<AlgoType>().end(), std::back_inserter(ret),
0141                     [](auto&& key_value_pair) { return key_value_pair.first(); });
0142     }
0143     return ret;
0144   }
0145 
0146 private:
0147   // Do we have any algorithms available of a certain type?
0148   template <class AlgoType> bool available() const { return !m_factories.count(typeid(AlgoType)); }
0149   // Do we have any algorithms of type AlgoType with a specific name available? Throws
0150   // if any of the lower-level assumptions fail
0151   template <class AlgoType> bool available(std::string_view name) const {
0152     if (name == "") {
0153       raise(fmt::format("Invalid name provided: '{}'", name));
0154     }
0155     return factory<AlgoType>().count(name) != 0;
0156   }
0157   template <class AlgoType> const auto& factory() const {
0158     if (!available<AlgoType>()) {
0159       raise(fmt::format("No factory for algorithm type {} provided", typeid(AlgoType).name()));
0160     }
0161     return m_factories.at(typeid(AlgoType));
0162   }
0163   // Throw an exception if we are not ready as factory to avoid giving incomplete factory
0164   // information to the caller
0165   void ensureReady() const {
0166     if (!ready()) {
0167       raise("Attempt to use AlgorithmSvc, but service not yet marked as ready");
0168     }
0169   }
0170 
0171   using factory_type = std::function<std::unique_ptr<AlgorithmBase>()>;
0172 
0173   std::map<std::type_index, std::map<std::string_view, factory_type>> m_factories;
0174 };
0175 
0176 namespace detail {
0177   template <class T> struct is_input : std::false_type {};
0178   template <class... T> struct is_input<Input<T...>> : std::true_type {};
0179   template <class T> struct is_output : std::false_type {};
0180   template <class... T> struct is_output<Output<T...>> : std::true_type {};
0181 } // namespace detail
0182 template <class T> constexpr bool is_input_v  = detail::is_input<T>::value;
0183 template <class T> constexpr bool is_output_v = detail::is_output<T>::value;
0184 
0185 } // namespace algorithms
0186