DCCL v5
Loading...
Searching...
No Matches
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 <stdexcept>
30
31#include <google/protobuf/compiler/importer.h>
32#include <google/protobuf/descriptor.h>
33#include <google/protobuf/descriptor.pb.h>
34#include <google/protobuf/descriptor_database.h>
35#include <google/protobuf/dynamic_message.h>
36
37#include <memory>
38
39#include "thread_safety.h"
40
41namespace dccl
42{
45{
46 public:
48 DynamicProtobufManager& operator=(const DynamicProtobufManager&) = delete;
49
60 static const google::protobuf::Descriptor*
61 find_descriptor(const std::string& protobuf_type_name, bool user_pool_first = false);
62
73 template <typename GoogleProtobufMessagePointer>
74 static GoogleProtobufMessagePointer new_protobuf_message(const std::string& protobuf_type_name,
75 bool user_pool_first = false)
76 {
77 DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
78
79 const google::protobuf::Descriptor* desc =
80 find_descriptor(protobuf_type_name, user_pool_first);
81 if (desc)
82 return new_protobuf_message<GoogleProtobufMessagePointer>(desc);
83 else
84 throw(std::runtime_error("Unknown type " + protobuf_type_name +
85 ", be sure it is loaded at compile-time, via dlopen, or with "
86 "a call to add_protobuf_file()"));
87 }
88
94 template <typename GoogleProtobufMessagePointer>
95 static GoogleProtobufMessagePointer
96 new_protobuf_message(const google::protobuf::Descriptor* desc)
97 {
98 DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
99 return GoogleProtobufMessagePointer(
100 get_instance()->msg_factory_->GetPrototype(desc)->New());
101 }
102
107 static std::shared_ptr<google::protobuf::Message>
108 new_protobuf_message(const google::protobuf::Descriptor* desc);
109
114 static std::shared_ptr<google::protobuf::Message>
115 new_protobuf_message(const std::string& protobuf_type_name);
116
118 static void add_database(std::shared_ptr<google::protobuf::DescriptorDatabase> database);
119
121 static void enable_compilation()
122 {
123 DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
124 get_instance()->enable_disk_source_database();
125 }
126
137 static const google::protobuf::FileDescriptor*
138 load_from_proto_file(const std::string& protofile_absolute_path);
139
143 static void add_include_path(const std::string& path);
144
148 static void* load_from_shared_lib(const std::string& shared_lib_path);
149
150 static void protobuf_shutdown()
151 {
152 DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
153 get_instance()->shutdown();
154 }
155
157 static const google::protobuf::FileDescriptor*
158 add_protobuf_file(const google::protobuf::FileDescriptorProto& proto);
159
160 static void reset()
161 {
162 DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
163 inst_.reset(new DynamicProtobufManager, DynamicProtobufManager::custom_deleter);
164 }
165
166 static void custom_deleter(DynamicProtobufManager* obj) { delete obj; }
167
168#if !(DCCL_THREAD_SUPPORT)
169 // no way to make these thread safe without the downstream user locking the mutex
170
171 static google::protobuf::DynamicMessageFactory& msg_factory()
172 {
173 return *get_instance()->msg_factory_;
174 }
175 static google::protobuf::DescriptorPool& user_descriptor_pool()
176 {
177 return *get_instance()->user_descriptor_pool_;
178 }
179 static google::protobuf::SimpleDescriptorDatabase& simple_database()
180 {
181 return *get_instance()->simple_database_;
182 }
183#else
184 template <typename T = void> static google::protobuf::DynamicMessageFactory& msg_factory()
185 {
186 static_assert(!std::is_same<T, T>::value,
187 "msg_factory() has been removed for thread-safety "
188 "reasons, use msg_factory_call(...) instead.");
189 // to suppress warning, will never actually be called
190 return *get_instance()->msg_factory_;
191 }
192 template <typename T = void> static google::protobuf::DescriptorPool& user_descriptor_pool()
193 {
194 static_assert(!std::is_same<T, T>::value,
195 "user_descriptor_pool() has been removed for thread-safety reasons, use "
196 "user_descriptor_pool_call(...) instead.");
197 // to suppress warning, will never actually be called
198 return *get_instance()->user_descriptor_pool_;
199 }
200 template <typename T = void>
201 static google::protobuf::SimpleDescriptorDatabase& simple_database()
202 {
203 static_assert(!std::is_same<T, T>::value,
204 "simple_database() has been removed for thread-safety reasons, use "
205 "simple_database_call(...) instead.");
206 // to suppress warning, will never actually be called
207 return *get_instance()->simple_database_;
208 }
209#endif
210
211 template <typename ReturnType, typename... Args1, typename... Args2>
212 static ReturnType
213 msg_factory_call(ReturnType (google::protobuf::DynamicMessageFactory::*func)(Args1...) const,
214 Args2... args)
215 {
216 DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
217 return ((*get_instance()->msg_factory_).*func)(args...);
218 }
219
220 template <typename ReturnType, typename... Args1, typename... Args2>
221 static ReturnType
222 user_descriptor_pool_call(ReturnType (google::protobuf::DescriptorPool::*func)(Args1...) const,
223 Args2... args)
224 {
225 DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
226 return ((*get_instance()->user_descriptor_pool_).*func)(args...);
227 }
228
229 template <typename ReturnType, typename... Args1, typename... Args2>
230 static ReturnType
231 simple_database_call(ReturnType (google::protobuf::SimpleDescriptorDatabase::*func)(Args1...)
232 const,
233 Args2... args)
234 {
235 DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
236 return ((*get_instance()->simple_database_).*func)(args...);
237 }
238
239 private:
240 static std::shared_ptr<DynamicProtobufManager> inst_;
241 static DynamicProtobufManager* get_instance()
242 {
243 DCCL_LOCK_DYNAMIC_PROTOBUF_MANAGER_MUTEX
244
245 if (!inst_)
246 inst_.reset(new DynamicProtobufManager, DynamicProtobufManager::custom_deleter);
247 return inst_.get();
248 }
249
250 DynamicProtobufManager()
251 : generated_database_(new google::protobuf::DescriptorPoolDatabase(
252 *google::protobuf::DescriptorPool::generated_pool())),
253 simple_database_(new google::protobuf::SimpleDescriptorDatabase),
254 msg_factory_(new google::protobuf::DynamicMessageFactory)
255 {
256 databases_.push_back(simple_database_);
257 databases_.push_back(generated_database_);
258
259 msg_factory_->SetDelegateToGeneratedFactory(true);
260
261 update_databases();
262 }
263
264 ~DynamicProtobufManager() = default;
265
266 void shutdown()
267 {
268 for (auto& dl_handle : dl_handles_) dlclose(dl_handle);
269 google::protobuf::ShutdownProtobufLibrary();
270 inst_.reset();
271 }
272
273 void update_databases()
274 {
275 std::vector<google::protobuf::DescriptorDatabase*> databases;
276
277 for (const auto& database : databases_) databases.push_back(database.get());
278
279 merged_database_.reset(new google::protobuf::MergedDescriptorDatabase(databases));
280 user_descriptor_pool_.reset(new google::protobuf::DescriptorPool(merged_database_.get()));
281 }
282
283 void enable_disk_source_database();
284
285 private:
286 std::vector<std::shared_ptr<google::protobuf::DescriptorDatabase>> databases_;
287
288 // always used
289 std::shared_ptr<google::protobuf::DescriptorPoolDatabase> generated_database_;
290 std::shared_ptr<google::protobuf::SimpleDescriptorDatabase> simple_database_;
291 std::shared_ptr<google::protobuf::MergedDescriptorDatabase> merged_database_;
292 std::shared_ptr<google::protobuf::DescriptorPool> user_descriptor_pool_;
293 std::shared_ptr<google::protobuf::DynamicMessageFactory> msg_factory_;
294
295 // sometimes used
296 std::shared_ptr<google::protobuf::compiler::DiskSourceTree> disk_source_tree_;
297 std::shared_ptr<google::protobuf::compiler::SourceTreeDescriptorDatabase> source_database_;
298
299 class DLogMultiFileErrorCollector : public google::protobuf::compiler::MultiFileErrorCollector
300 {
301
302#if GOOGLE_PROTOBUF_VERSION >= 4022000
303 void RecordError(absl::string_view filename, int line, int column,
304 absl::string_view message) override;
305#else
306 void AddError(const std::string& filename, int line, int column,
307 const std::string& message) override;
308#endif
309 };
310
311 std::shared_ptr<DLogMultiFileErrorCollector> error_collector_;
312
313 std::vector<void*> dl_handles_;
314};
315
316} // namespace dccl
317
318#endif
Helper class for creating google::protobuf::Message objects that are not statically compiled into the...
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.
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()
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)
static void enable_compilation()
Enable on the fly compilation of .proto files on the local disk. Must be called before load_from_prot...
static GoogleProtobufMessagePointer new_protobuf_message(const google::protobuf::Descriptor *desc)
Create a new (empty) Google Protobuf message of a given type by 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...
static const google::protobuf::FileDescriptor * add_protobuf_file(const google::protobuf::FileDescriptorProto &proto)
Add a protobuf file defined in a google::protobuf::FileDescriptorProto.
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.
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.
Dynamic Compact Control Language namespace.
Definition any.h:28