DCCL v4
dynamic_protobuf_manager.h
1 // Copyright 2012-2023:
2 // GobySoft, LLC (2013-)
3 // Massachusetts Institute of Technology (2007-2014)
4 // Community contributors (see AUTHORS file)
5 // File authors:
6 // Toby Schneider <toby@gobysoft.org>
7 //
8 //
9 // This file is part of the Dynamic Compact Control Language Library
10 // ("DCCL").
11 //
12 // DCCL is free software: you can redistribute it and/or modify
13 // it under the terms of the GNU Lesser General Public License as published by
14 // the Free Software Foundation, either version 2.1 of the License, or
15 // (at your option) any later version.
16 //
17 // DCCL is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU Lesser General Public License for more details.
21 //
22 // You should have received a copy of the GNU Lesser General Public License
23 // along with DCCL. If not, see <http://www.gnu.org/licenses/>.
24 #ifndef DCCLDYNAMICPROTOBUFMANAGER20110419H
25 #define DCCLDYNAMICPROTOBUFMANAGER20110419H
26 
27 #include <dlfcn.h>
28 
29 #include <iostream>
30 #include <set>
31 #include <stdexcept>
32 
33 #include <google/protobuf/compiler/importer.h>
34 #include <google/protobuf/descriptor.h>
35 #include <google/protobuf/descriptor.pb.h>
36 #include <google/protobuf/descriptor_database.h>
37 #include <google/protobuf/dynamic_message.h>
38 
39 #include <memory>
40 
41 #include "thread_safety.h"
42 
43 namespace dccl
44 {
47 {
48  public:
50  DynamicProtobufManager& operator=(const DynamicProtobufManager&) = delete;
51 
62  static const google::protobuf::Descriptor*
63  find_descriptor(const std::string& protobuf_type_name, bool user_pool_first = false);
64 
75  template <typename GoogleProtobufMessagePointer>
76  static GoogleProtobufMessagePointer new_protobuf_message(const std::string& protobuf_type_name,
77  bool user_pool_first = false)
78  {
79  DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
80 
81  const google::protobuf::Descriptor* desc =
82  find_descriptor(protobuf_type_name, user_pool_first);
83  if (desc)
84  return new_protobuf_message<GoogleProtobufMessagePointer>(desc);
85  else
86  throw(std::runtime_error("Unknown type " + protobuf_type_name +
87  ", be sure it is loaded at compile-time, via dlopen, or with "
88  "a call to add_protobuf_file()"));
89  }
90 
96  template <typename GoogleProtobufMessagePointer>
97  static GoogleProtobufMessagePointer
98  new_protobuf_message(const google::protobuf::Descriptor* desc)
99  {
100  DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
101  return GoogleProtobufMessagePointer(
102  get_instance()->msg_factory_->GetPrototype(desc)->New());
103  }
104 
109  static std::shared_ptr<google::protobuf::Message>
110  new_protobuf_message(const google::protobuf::Descriptor* desc);
111 
116  static std::shared_ptr<google::protobuf::Message>
117  new_protobuf_message(const std::string& protobuf_type_name);
118 
120  static void add_database(std::shared_ptr<google::protobuf::DescriptorDatabase> database);
121 
123  static void enable_compilation()
124  {
125  DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
126  get_instance()->enable_disk_source_database();
127  }
128 
139  static const google::protobuf::FileDescriptor*
140  load_from_proto_file(const std::string& protofile_absolute_path);
141 
145  static void add_include_path(const std::string& path);
146 
150  static void* load_from_shared_lib(const std::string& shared_lib_path);
151 
152  static void protobuf_shutdown()
153  {
154  DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
155  get_instance()->shutdown();
156  }
157 
159  static const google::protobuf::FileDescriptor*
160  add_protobuf_file(const google::protobuf::FileDescriptorProto& proto);
161 
162  static void reset()
163  {
164  DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
165  inst_.reset(new DynamicProtobufManager, DynamicProtobufManager::custom_deleter);
166  }
167 
168  static void custom_deleter(DynamicProtobufManager* obj) { delete obj; }
169 
170 #if !(DCCL_THREAD_SUPPORT)
171  // no way to make these thread safe without the downstream user locking the mutex
172 
173  static google::protobuf::DynamicMessageFactory& msg_factory()
174  {
175  return *get_instance()->msg_factory_;
176  }
177  static google::protobuf::DescriptorPool& user_descriptor_pool()
178  {
179  return *get_instance()->user_descriptor_pool_;
180  }
181  static google::protobuf::SimpleDescriptorDatabase& simple_database()
182  {
183  return *get_instance()->simple_database_;
184  }
185 #else
186  template <typename T = void> static google::protobuf::DynamicMessageFactory& msg_factory()
187  {
188  static_assert(!std::is_same<T, T>::value,
189  "msg_factory() has been removed for thread-safety "
190  "reasons, use msg_factory_call(...) instead.");
191  // to suppress warning, will never actually be called
192  return *get_instance()->msg_factory_;
193  }
194  template <typename T = void> static google::protobuf::DescriptorPool& user_descriptor_pool()
195  {
196  static_assert(!std::is_same<T, T>::value,
197  "user_descriptor_pool() has been removed for thread-safety reasons, use "
198  "user_descriptor_pool_call(...) instead.");
199  // to suppress warning, will never actually be called
200  return *get_instance()->user_descriptor_pool_;
201  }
202  template <typename T = void>
203  static google::protobuf::SimpleDescriptorDatabase& simple_database()
204  {
205  static_assert(!std::is_same<T, T>::value,
206  "simple_database() has been removed for thread-safety reasons, use "
207  "simple_database_call(...) instead.");
208  // to suppress warning, will never actually be called
209  return *get_instance()->simple_database_;
210  }
211 #endif
212 
213  template <typename ReturnType, typename... Args1, typename... Args2>
214  static ReturnType
215  msg_factory_call(ReturnType (google::protobuf::DynamicMessageFactory::*func)(Args1...) const,
216  Args2... args)
217  {
218  DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
219  return ((*get_instance()->msg_factory_).*func)(args...);
220  }
221 
222  template <typename ReturnType, typename... Args1, typename... Args2>
223  static ReturnType
224  user_descriptor_pool_call(ReturnType (google::protobuf::DescriptorPool::*func)(Args1...) const,
225  Args2... args)
226  {
227  DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
228  return ((*get_instance()->user_descriptor_pool_).*func)(args...);
229  }
230 
231  template <typename ReturnType, typename... Args1, typename... Args2>
232  static ReturnType
233  simple_database_call(ReturnType (google::protobuf::SimpleDescriptorDatabase::*func)(Args1...)
234  const,
235  Args2... args)
236  {
237  DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
238  return ((*get_instance()->simple_database_).*func)(args...);
239  }
240 
241  private:
242  static std::shared_ptr<DynamicProtobufManager> inst_;
243  static DynamicProtobufManager* get_instance()
244  {
245  DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
246 
247  if (!inst_)
248  inst_.reset(new DynamicProtobufManager, DynamicProtobufManager::custom_deleter);
249  return inst_.get();
250  }
251 
252  DynamicProtobufManager()
253  : generated_database_(new google::protobuf::DescriptorPoolDatabase(
254  *google::protobuf::DescriptorPool::generated_pool())),
255  simple_database_(new google::protobuf::SimpleDescriptorDatabase),
256  msg_factory_(new google::protobuf::DynamicMessageFactory)
257  {
258  databases_.push_back(simple_database_);
259  databases_.push_back(generated_database_);
260 
261  msg_factory_->SetDelegateToGeneratedFactory(true);
262 
263  update_databases();
264  }
265 
266  ~DynamicProtobufManager() = default;
267 
268  void shutdown()
269  {
270  for (auto& dl_handle : dl_handles_) dlclose(dl_handle);
271  google::protobuf::ShutdownProtobufLibrary();
272  inst_.reset();
273  }
274 
275  void update_databases()
276  {
277  std::vector<google::protobuf::DescriptorDatabase*> databases;
278 
279  for (const auto& database : databases_) databases.push_back(database.get());
280 
281  merged_database_.reset(new google::protobuf::MergedDescriptorDatabase(databases));
282  user_descriptor_pool_.reset(new google::protobuf::DescriptorPool(merged_database_.get()));
283  }
284 
285  void enable_disk_source_database();
286 
287  private:
288  std::vector<std::shared_ptr<google::protobuf::DescriptorDatabase>> databases_;
289 
290  // always used
291  std::shared_ptr<google::protobuf::DescriptorPoolDatabase> generated_database_;
292  std::shared_ptr<google::protobuf::SimpleDescriptorDatabase> simple_database_;
293  std::shared_ptr<google::protobuf::MergedDescriptorDatabase> merged_database_;
294  std::shared_ptr<google::protobuf::DescriptorPool> user_descriptor_pool_;
295  std::shared_ptr<google::protobuf::DynamicMessageFactory> msg_factory_;
296 
297  // sometimes used
298  std::shared_ptr<google::protobuf::compiler::DiskSourceTree> disk_source_tree_;
299  std::shared_ptr<google::protobuf::compiler::SourceTreeDescriptorDatabase> source_database_;
300 
301  class DLogMultiFileErrorCollector : public google::protobuf::compiler::MultiFileErrorCollector
302  {
303  void AddError(const std::string& filename, int line, int column,
304  const std::string& message) override;
305  };
306 
307  std::shared_ptr<DLogMultiFileErrorCollector> error_collector_;
308 
309  std::vector<void*> dl_handles_;
310 };
311 
312 } // namespace dccl
313 
314 #endif
dccl::DynamicProtobufManager::add_protobuf_file
static const google::protobuf::FileDescriptor * add_protobuf_file(const google::protobuf::FileDescriptorProto &proto)
Add a protobuf file defined in a google::protobuf::FileDescriptorProto.
Definition: dynamic_protobuf_manager.cpp:124
dccl
Dynamic Compact Control Language namespace.
Definition: any.h:46
dccl::DynamicProtobufManager
Helper class for creating google::protobuf::Message objects that are not statically compiled into the...
Definition: dynamic_protobuf_manager.h:46
dccl::DynamicProtobufManager::new_protobuf_message
static GoogleProtobufMessagePointer new_protobuf_message(const google::protobuf::Descriptor *desc)
Create a new (empty) Google Protobuf message of a given type by Descriptor.
Definition: dynamic_protobuf_manager.h:98
dccl::DynamicProtobufManager::load_from_shared_lib
static void * load_from_shared_lib(const std::string &shared_lib_path)
Load compiled .proto files from a UNIX shared library (i.e. *.so or *.dylib)
Definition: dynamic_protobuf_manager.cpp:97
dccl::DynamicProtobufManager::add_database
static void add_database(std::shared_ptr< google::protobuf::DescriptorDatabase > database)
Add a Google Protobuf DescriptorDatabase to the set of databases searched for Message Descriptors.
Definition: dynamic_protobuf_manager.cpp:78
dccl::DynamicProtobufManager::load_from_proto_file
static const google::protobuf::FileDescriptor * load_from_proto_file(const std::string &protofile_absolute_path)
Load a message from a .proto file on the disk. enable_compilation() must be called first.
Definition: dynamic_protobuf_manager.cpp:112
dccl::DynamicProtobufManager::enable_compilation
static void enable_compilation()
Enable on the fly compilation of .proto files on the local disk. Must be called before load_from_prot...
Definition: dynamic_protobuf_manager.h:123
dccl::DynamicProtobufManager::add_include_path
static void add_include_path(const std::string &path)
Add a path for searching for import messages when loading .proto files using load_from_proto_file()
Definition: dynamic_protobuf_manager.cpp:86
dccl::DynamicProtobufManager::new_protobuf_message
static GoogleProtobufMessagePointer new_protobuf_message(const std::string &protobuf_type_name, bool user_pool_first=false)
Create a new (empty) Google Protobuf message of a given type by name.
Definition: dynamic_protobuf_manager.h:76
MultiFileErrorCollector
dccl::DynamicProtobufManager::find_descriptor
static const google::protobuf::Descriptor * find_descriptor(const std::string &protobuf_type_name, bool user_pool_first=false)
Finds the Google Protobuf Descriptor (essentially a meta-class for a given Message) from a given Mess...
Definition: dynamic_protobuf_manager.cpp:36