DCCL v3
dynamic_protobuf_manager.h
1 // Copyright 2009-2017 Toby Schneider (http://gobysoft.org/index.wt/people/toby)
2 // GobySoft, LLC (for 2013-)
3 // Massachusetts Institute of Technology (for 2007-2014)
4 // Community contributors (see AUTHORS file)
5 //
6 //
7 // This file is part of the Dynamic Compact Control Language Library
8 // ("DCCL").
9 //
10 // DCCL is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 2.1 of the License, or
13 // (at your option) any later version.
14 //
15 // DCCL is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with DCCL. If not, see <http://www.gnu.org/licenses/>.
22 #ifndef DCCLDYNAMICPROTOBUFMANAGER20110419H
23 #define DCCLDYNAMICPROTOBUFMANAGER20110419H
24 
25 #include <dlfcn.h>
26 
27 #include <set>
28 #include <stdexcept>
29 #include <iostream>
30 
31 #include <google/protobuf/descriptor.h>
32 #include <google/protobuf/descriptor.pb.h>
33 #include <google/protobuf/dynamic_message.h>
34 #include <google/protobuf/descriptor_database.h>
35 #include <google/protobuf/compiler/importer.h>
36 
37 #include <boost/shared_ptr.hpp>
38 #include <boost/version.hpp>
39 
40 namespace dccl
41 {
44  {
45  public:
54  static const google::protobuf::Descriptor* find_descriptor(const std::string& protobuf_type_name)
55  {
56  // try the generated pool
57  const google::protobuf::Descriptor* desc = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(protobuf_type_name);
58  if(desc) return desc;
59 
60  // try the user pool
61  desc = user_descriptor_pool().FindMessageTypeByName(protobuf_type_name);
62  return desc;
63  }
64 
75  template<typename GoogleProtobufMessagePointer>
76  static GoogleProtobufMessagePointer new_protobuf_message(
77  const std::string& protobuf_type_name)
78  {
79  const google::protobuf::Descriptor* desc = find_descriptor(protobuf_type_name);
80  if(desc)
81  return new_protobuf_message<GoogleProtobufMessagePointer>(desc);
82  else
83  throw(std::runtime_error("Unknown type " + protobuf_type_name + ", be sure it is loaded at compile-time, via dlopen, or with a call to add_protobuf_file()"));
84  }
85 
91  template<typename GoogleProtobufMessagePointer>
92  static GoogleProtobufMessagePointer new_protobuf_message(
93  const google::protobuf::Descriptor* desc)
94  { return GoogleProtobufMessagePointer(msg_factory().GetPrototype(desc)->New()); }
95 
100  static boost::shared_ptr<google::protobuf::Message> new_protobuf_message(
101  const google::protobuf::Descriptor* desc)
102  { return new_protobuf_message<boost::shared_ptr<google::protobuf::Message> >(desc); }
103 
108  static boost::shared_ptr<google::protobuf::Message> new_protobuf_message(
109  const std::string& protobuf_type_name)
110  { return new_protobuf_message<boost::shared_ptr<google::protobuf::Message> >(protobuf_type_name); }
111 
112 
114  static void add_database(boost::shared_ptr<google::protobuf::DescriptorDatabase> database)
115  {
116  get_instance()->databases_.push_back(database);
117  get_instance()->update_databases();
118  }
119 
121  static void enable_compilation()
122  {
123  get_instance()->enable_disk_source_database();
124  }
125 
136  static const google::protobuf::FileDescriptor*
137  load_from_proto_file(const std::string& protofile_absolute_path);
138 
139 
143  static void add_include_path(const std::string& path)
144  {
145  if(!get_instance()->disk_source_tree_)
146  throw(std::runtime_error("Must called enable_compilation() before loading proto files directly"));
147 
148  get_instance()->disk_source_tree_->MapPath("", path);
149  }
150 
154  static void* load_from_shared_lib(const std::string& shared_lib_path)
155  {
156  void* handle = dlopen(shared_lib_path.c_str(), RTLD_LAZY);
157  if(handle)
158  get_instance()->dl_handles_.push_back(handle);
159  return handle;
160  }
161 
162  static void protobuf_shutdown()
163  {
164  get_instance()->shutdown();
165  }
166 
167 
169  static const google::protobuf::FileDescriptor* add_protobuf_file(
170  const google::protobuf::FileDescriptorProto& proto);
171 
172  static google::protobuf::DynamicMessageFactory& msg_factory()
173  { return *get_instance()->msg_factory_; }
174  static google::protobuf::DescriptorPool& user_descriptor_pool()
175  { return *get_instance()->user_descriptor_pool_; }
176  static google::protobuf::SimpleDescriptorDatabase& simple_database()
177  { return *get_instance()->simple_database_; }
178 
179  static void reset()
180  {
181  inst_.reset(new DynamicProtobufManager);
182  }
183 
184 
185  private:
186 
187  // so we can use shared_ptr to hold the singleton
188 #if BOOST_VERSION >= 107000
189  template<typename T>
190  friend void boost::checked_delete(T*) BOOST_NOEXCEPT;
191 #else
192  template<typename T>
193  friend void boost::checked_delete(T*);
194 #endif
195 
196  static boost::shared_ptr<DynamicProtobufManager> inst_;
197 
198  static DynamicProtobufManager* get_instance()
199  {
200  if(!inst_)
201  inst_.reset(new DynamicProtobufManager);
202  return inst_.get();
203  }
204 
205 
206  DynamicProtobufManager()
207  : generated_database_(new google::protobuf::DescriptorPoolDatabase(*google::protobuf::DescriptorPool::generated_pool())),
208  simple_database_(new google::protobuf::SimpleDescriptorDatabase),
209  msg_factory_(new google::protobuf::DynamicMessageFactory)
210  {
211  databases_.push_back(simple_database_);
212  databases_.push_back(generated_database_);
213 
214  msg_factory_->SetDelegateToGeneratedFactory(true);
215 
216  update_databases();
217  }
218 
219  ~DynamicProtobufManager()
220  {
221  }
222 
223 
224  void shutdown()
225  {
226  for(std::vector<void *>::iterator it = dl_handles_.begin(),
227  n = dl_handles_.end(); it != n; ++it)
228  dlclose(*it);
229  google::protobuf::ShutdownProtobufLibrary();
230  inst_.reset();
231  }
232 
233 
234  void update_databases()
235  {
236  std::vector<google::protobuf::DescriptorDatabase*> databases;
237 
238  for(std::vector<boost::shared_ptr<google::protobuf::DescriptorDatabase> >::const_iterator it = databases_.begin(), end = databases_.end(); it != end; ++it)
239  databases.push_back(it->get());
240 
241  merged_database_.reset(new google::protobuf::MergedDescriptorDatabase(databases));
242  user_descriptor_pool_.reset(new google::protobuf::DescriptorPool(merged_database_.get()));
243  }
244 
245  void enable_disk_source_database();
246 
247  DynamicProtobufManager(const DynamicProtobufManager&);
248  DynamicProtobufManager& operator= (const DynamicProtobufManager&);
249 
250  private:
251  std::vector<boost::shared_ptr<google::protobuf::DescriptorDatabase> > databases_;
252 
253  // always used
254  boost::shared_ptr<google::protobuf::DescriptorPoolDatabase> generated_database_;
255  boost::shared_ptr<google::protobuf::SimpleDescriptorDatabase> simple_database_;
256  boost::shared_ptr<google::protobuf::MergedDescriptorDatabase> merged_database_;
257  boost::shared_ptr<google::protobuf::DescriptorPool> user_descriptor_pool_;
258  boost::shared_ptr<google::protobuf::DynamicMessageFactory> msg_factory_;
259 
260  // sometimes used
261  boost::shared_ptr<google::protobuf::compiler::DiskSourceTree> disk_source_tree_;
262  boost::shared_ptr<google::protobuf::compiler::SourceTreeDescriptorDatabase> source_database_;
263 
264  class DLogMultiFileErrorCollector
266  {
267  void AddError(const std::string & filename, int line, int column,
268  const std::string & message);
269  };
270 
271  boost::shared_ptr<DLogMultiFileErrorCollector> error_collector_;
272 
273  std::vector<void *> dl_handles_;
274  };
275 
276 }
277 
278 #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:30
dccl
Dynamic Compact Control Language namespace.
Definition: gen_units_class_plugin.h:49
dccl::DynamicProtobufManager
Helper class for creating google::protobuf::Message objects that are not statically compiled into the...
Definition: dynamic_protobuf_manager.h:43
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:92
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.h:143
dccl::DynamicProtobufManager::new_protobuf_message
static boost::shared_ptr< google::protobuf::Message > 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:100
dccl::DynamicProtobufManager::find_descriptor
static const google::protobuf::Descriptor * find_descriptor(const std::string &protobuf_type_name)
Finds the Google Protobuf Descriptor (essentially a meta-class for a given Message) from a given Mess...
Definition: dynamic_protobuf_manager.h:54
dccl::DynamicProtobufManager::new_protobuf_message
static boost::shared_ptr< google::protobuf::Message > new_protobuf_message(const std::string &protobuf_type_name)
Create a new (empty) Google Protobuf message of a given type by name.
Definition: dynamic_protobuf_manager.h:108
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:60
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.h:154
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:121
dccl::DynamicProtobufManager::add_database
static void add_database(boost::shared_ptr< google::protobuf::DescriptorDatabase > database)
Add a Google Protobuf DescriptorDatabase to the set of databases searched for Message Descriptors.
Definition: dynamic_protobuf_manager.h:114
dccl::DynamicProtobufManager::new_protobuf_message
static GoogleProtobufMessagePointer new_protobuf_message(const std::string &protobuf_type_name)
Create a new (empty) Google Protobuf message of a given type by name.
Definition: dynamic_protobuf_manager.h:76
MultiFileErrorCollector