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