1 // Copyright (c) 2016-present, Facebook, Inc. All rights reserved. 2 // This source code is licensed under both the GPLv2 (found in the 3 // COPYING file in the root directory) and Apache 2.0 License 4 // (found in the LICENSE.Apache file in the root directory). 5 6 #pragma once 7 8 #ifndef ROCKSDB_LITE 9 10 #include <functional> 11 #include <memory> 12 #include <regex> 13 #include <string> 14 #include <unordered_map> 15 #include <vector> 16 #include "rocksdb/status.h" 17 18 namespace ROCKSDB_NAMESPACE { 19 class Logger; 20 // Returns a new T when called with a string. Populates the std::unique_ptr 21 // argument if granting ownership to caller. 22 template <typename T> 23 using FactoryFunc = 24 std::function<T*(const std::string&, std::unique_ptr<T>*, std::string*)>; 25 26 class ObjectLibrary { 27 public: 28 // Base class for an Entry in the Registry. 29 class Entry { 30 public: ~Entry()31 virtual ~Entry() {} Entry(const std::string & name)32 Entry(const std::string& name) : name_(std::move(name)) {} 33 34 // Checks to see if the target matches this entry matches(const std::string & target)35 virtual bool matches(const std::string& target) const { 36 return name_ == target; 37 } Name()38 const std::string& Name() const { return name_; } 39 40 private: 41 const std::string name_; // The name of the Entry 42 }; // End class Entry 43 44 // An Entry containing a FactoryFunc for creating new Objects 45 template <typename T> 46 class FactoryEntry : public Entry { 47 public: FactoryEntry(const std::string & name,FactoryFunc<T> f)48 FactoryEntry(const std::string& name, FactoryFunc<T> f) 49 : Entry(name), pattern_(std::move(name)), factory_(std::move(f)) {} ~FactoryEntry()50 ~FactoryEntry() override {} matches(const std::string & target)51 bool matches(const std::string& target) const override { 52 return std::regex_match(target, pattern_); 53 } 54 // Creates a new T object. NewFactoryObject(const std::string & target,std::unique_ptr<T> * guard,std::string * msg)55 T* NewFactoryObject(const std::string& target, std::unique_ptr<T>* guard, 56 std::string* msg) const { 57 return factory_(target, guard, msg); 58 } 59 60 private: 61 std::regex pattern_; // The pattern for this entry 62 FactoryFunc<T> factory_; 63 }; // End class FactoryEntry 64 public: 65 // Finds the entry matching the input name and type 66 const Entry* FindEntry(const std::string& type, 67 const std::string& name) const; 68 void Dump(Logger* logger) const; 69 70 // Registers the factory with the library for the pattern. 71 // If the pattern matches, the factory may be used to create a new object. 72 template <typename T> Register(const std::string & pattern,const FactoryFunc<T> & factory)73 const FactoryFunc<T>& Register(const std::string& pattern, 74 const FactoryFunc<T>& factory) { 75 std::unique_ptr<Entry> entry(new FactoryEntry<T>(pattern, factory)); 76 AddEntry(T::Type(), entry); 77 return factory; 78 } 79 // Returns the default ObjectLibrary 80 static std::shared_ptr<ObjectLibrary>& Default(); 81 82 private: 83 // Adds the input entry to the list for the given type 84 void AddEntry(const std::string& type, std::unique_ptr<Entry>& entry); 85 86 // ** FactoryFunctions for this loader, organized by type 87 std::unordered_map<std::string, std::vector<std::unique_ptr<Entry>>> entries_; 88 }; 89 90 // The ObjectRegistry is used to register objects that can be created by a 91 // name/pattern at run-time where the specific implementation of the object may 92 // not be known in advance. 93 class ObjectRegistry { 94 public: 95 static std::shared_ptr<ObjectRegistry> NewInstance(); 96 97 ObjectRegistry(); 98 AddLibrary(const std::shared_ptr<ObjectLibrary> & library)99 void AddLibrary(const std::shared_ptr<ObjectLibrary>& library) { 100 libraries_.emplace_back(library); 101 } 102 103 // Creates a new T using the factory function that was registered with a 104 // pattern that matches the provided "target" string according to 105 // std::regex_match. 106 // 107 // If no registered functions match, returns nullptr. If multiple functions 108 // match, the factory function used is unspecified. 109 // 110 // Populates res_guard with result pointer if caller is granted ownership. 111 template <typename T> NewObject(const std::string & target,std::unique_ptr<T> * guard,std::string * errmsg)112 T* NewObject(const std::string& target, std::unique_ptr<T>* guard, 113 std::string* errmsg) { 114 guard->reset(); 115 const auto* basic = FindEntry(T::Type(), target); 116 if (basic != nullptr) { 117 const auto* factory = 118 static_cast<const ObjectLibrary::FactoryEntry<T>*>(basic); 119 return factory->NewFactoryObject(target, guard, errmsg); 120 } else { 121 *errmsg = std::string("Could not load ") + T::Type(); 122 return nullptr; 123 } 124 } 125 126 // Creates a new unique T using the input factory functions. 127 // Returns OK if a new unique T was successfully created 128 // Returns NotFound if the type/target could not be created 129 // Returns InvalidArgument if the factory return an unguarded object 130 // (meaning it cannot be managed by a unique ptr) 131 template <typename T> NewUniqueObject(const std::string & target,std::unique_ptr<T> * result)132 Status NewUniqueObject(const std::string& target, 133 std::unique_ptr<T>* result) { 134 std::string errmsg; 135 T* ptr = NewObject(target, result, &errmsg); 136 if (ptr == nullptr) { 137 return Status::NotFound(errmsg, target); 138 } else if (*result) { 139 return Status::OK(); 140 } else { 141 return Status::InvalidArgument(std::string("Cannot make a unique ") + 142 T::Type() + " from unguarded one ", 143 target); 144 } 145 } 146 147 // Creates a new shared T using the input factory functions. 148 // Returns OK if a new shared T was successfully created 149 // Returns NotFound if the type/target could not be created 150 // Returns InvalidArgument if the factory return an unguarded object 151 // (meaning it cannot be managed by a shared ptr) 152 template <typename T> NewSharedObject(const std::string & target,std::shared_ptr<T> * result)153 Status NewSharedObject(const std::string& target, 154 std::shared_ptr<T>* result) { 155 std::string errmsg; 156 std::unique_ptr<T> guard; 157 T* ptr = NewObject(target, &guard, &errmsg); 158 if (ptr == nullptr) { 159 return Status::NotFound(errmsg, target); 160 } else if (guard) { 161 result->reset(guard.release()); 162 return Status::OK(); 163 } else { 164 return Status::InvalidArgument(std::string("Cannot make a shared ") + 165 T::Type() + " from unguarded one ", 166 target); 167 } 168 } 169 170 // Creates a new static T using the input factory functions. 171 // Returns OK if a new static T was successfully created 172 // Returns NotFound if the type/target could not be created 173 // Returns InvalidArgument if the factory return a guarded object 174 // (meaning it is managed by a unique ptr) 175 template <typename T> NewStaticObject(const std::string & target,T ** result)176 Status NewStaticObject(const std::string& target, T** result) { 177 std::string errmsg; 178 std::unique_ptr<T> guard; 179 T* ptr = NewObject(target, &guard, &errmsg); 180 if (ptr == nullptr) { 181 return Status::NotFound(errmsg, target); 182 } else if (guard.get()) { 183 return Status::InvalidArgument(std::string("Cannot make a static ") + 184 T::Type() + " from a guarded one ", 185 target); 186 } else { 187 *result = ptr; 188 return Status::OK(); 189 } 190 } 191 192 // Dump the contents of the registry to the logger 193 void Dump(Logger* logger) const; 194 195 private: 196 const ObjectLibrary::Entry* FindEntry(const std::string& type, 197 const std::string& name) const; 198 199 // The set of libraries to search for factories for this registry. 200 // The libraries are searched in reverse order (back to front) when 201 // searching for entries. 202 std::vector<std::shared_ptr<ObjectLibrary>> libraries_; 203 }; 204 } // namespace ROCKSDB_NAMESPACE 205 #endif // ROCKSDB_LITE 206