DCCL v4
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 <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
43namespace 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
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:47