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 
39 namespace dccl
40 {
43  {
44  public:
53  static const google::protobuf::Descriptor* find_descriptor(const std::string& protobuf_type_name)
54  {
55  // try the generated pool
56  const google::protobuf::Descriptor* desc = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName(protobuf_type_name);
57  if(desc) return desc;
58 
59  // try the user pool
60  desc = user_descriptor_pool().FindMessageTypeByName(protobuf_type_name);
61  return desc;
62  }
63 
74  template<typename GoogleProtobufMessagePointer>
75  static GoogleProtobufMessagePointer new_protobuf_message(
76  const std::string& protobuf_type_name)
77  {
78  const google::protobuf::Descriptor* desc = find_descriptor(protobuf_type_name);
79  if(desc)
80  return new_protobuf_message<GoogleProtobufMessagePointer>(desc);
81  else
82  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()"));
83  }
84 
90  template<typename GoogleProtobufMessagePointer>
91  static GoogleProtobufMessagePointer new_protobuf_message(
92  const google::protobuf::Descriptor* desc)
93  { return GoogleProtobufMessagePointer(msg_factory().GetPrototype(desc)->New()); }
94 
99  static boost::shared_ptr<google::protobuf::Message> new_protobuf_message(
100  const google::protobuf::Descriptor* desc)
101  { return new_protobuf_message<boost::shared_ptr<google::protobuf::Message> >(desc); }
102 
107  static boost::shared_ptr<google::protobuf::Message> new_protobuf_message(
108  const std::string& protobuf_type_name)
109  { return new_protobuf_message<boost::shared_ptr<google::protobuf::Message> >(protobuf_type_name); }
110 
111 
113  static void add_database(boost::shared_ptr<google::protobuf::DescriptorDatabase> database)
114  {
115  get_instance()->databases_.push_back(database);
116  get_instance()->update_databases();
117  }
118 
120  static void enable_compilation()
121  {
122  get_instance()->enable_disk_source_database();
123  }
124 
135  static const google::protobuf::FileDescriptor*
136  load_from_proto_file(const std::string& protofile_absolute_path);
137 
138 
142  static void add_include_path(const std::string& path)
143  {
144  if(!get_instance()->disk_source_tree_)
145  throw(std::runtime_error("Must called enable_compilation() before loading proto files directly"));
146 
147  get_instance()->disk_source_tree_->MapPath("", path);
148  }
149 
153  static void* load_from_shared_lib(const std::string& shared_lib_path)
154  {
155  void* handle = dlopen(shared_lib_path.c_str(), RTLD_LAZY);
156  if(handle)
157  get_instance()->dl_handles_.push_back(handle);
158  return handle;
159  }
160 
161  static void protobuf_shutdown()
162  {
163  get_instance()->shutdown();
164  }
165 
166 
168  static const google::protobuf::FileDescriptor* add_protobuf_file(
169  const google::protobuf::FileDescriptorProto& proto);
170 
171  static google::protobuf::DynamicMessageFactory& msg_factory()
172  { return *get_instance()->msg_factory_; }
173  static google::protobuf::DescriptorPool& user_descriptor_pool()
174  { return *get_instance()->user_descriptor_pool_; }
175  static google::protobuf::SimpleDescriptorDatabase& simple_database()
176  { return *get_instance()->simple_database_; }
177 
178  static void reset()
179  {
180  inst_.reset(new DynamicProtobufManager);
181  }
182 
183 
184  private:
185  // so we can use shared_ptr to hold the singleton
186  template<typename T>
187  friend void boost::checked_delete(T*);
188  static boost::shared_ptr<DynamicProtobufManager> inst_;
189 
190  static DynamicProtobufManager* get_instance()
191  {
192  if(!inst_)
193  inst_.reset(new DynamicProtobufManager);
194  return inst_.get();
195  }
196 
197 
199  : generated_database_(new google::protobuf::DescriptorPoolDatabase(*google::protobuf::DescriptorPool::generated_pool())),
200  simple_database_(new google::protobuf::SimpleDescriptorDatabase),
201  msg_factory_(new google::protobuf::DynamicMessageFactory)
202  {
203  databases_.push_back(simple_database_);
204  databases_.push_back(generated_database_);
205 
206  msg_factory_->SetDelegateToGeneratedFactory(true);
207 
208  update_databases();
209  }
210 
212  {
213  }
214 
215 
216  void shutdown()
217  {
218  for(std::vector<void *>::iterator it = dl_handles_.begin(),
219  n = dl_handles_.end(); it != n; ++it)
220  dlclose(*it);
221  google::protobuf::ShutdownProtobufLibrary();
222  inst_.reset();
223  }
224 
225 
226  void update_databases()
227  {
228  std::vector<google::protobuf::DescriptorDatabase*> databases;
229 
230  for(std::vector<boost::shared_ptr<google::protobuf::DescriptorDatabase> >::const_iterator it = databases_.begin(), end = databases_.end(); it != end; ++it)
231  databases.push_back(it->get());
232 
233  merged_database_.reset(new google::protobuf::MergedDescriptorDatabase(databases));
234  user_descriptor_pool_.reset(new google::protobuf::DescriptorPool(merged_database_.get()));
235  }
236 
237  void enable_disk_source_database();
238 
241 
242  private:
243  std::vector<boost::shared_ptr<google::protobuf::DescriptorDatabase> > databases_;
244 
245  // always used
246  boost::shared_ptr<google::protobuf::DescriptorPoolDatabase> generated_database_;
247  boost::shared_ptr<google::protobuf::SimpleDescriptorDatabase> simple_database_;
248  boost::shared_ptr<google::protobuf::MergedDescriptorDatabase> merged_database_;
249  boost::shared_ptr<google::protobuf::DescriptorPool> user_descriptor_pool_;
250  boost::shared_ptr<google::protobuf::DynamicMessageFactory> msg_factory_;
251 
252  // sometimes used
253  boost::shared_ptr<google::protobuf::compiler::DiskSourceTree> disk_source_tree_;
254  boost::shared_ptr<google::protobuf::compiler::SourceTreeDescriptorDatabase> source_database_;
255 
256  class DLogMultiFileErrorCollector
258  {
259  void AddError(const std::string & filename, int line, int column,
260  const std::string & message);
261  };
262 
263  boost::shared_ptr<DLogMultiFileErrorCollector> error_collector_;
264 
265  std::vector<void *> dl_handles_;
266  };
267 
268 }
269 
270 #endif
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 std::string &protobuf_type_name)
Create a new (empty) Google Protobuf message of a given type by name.
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...
static const google::protobuf::FileDescriptor * add_protobuf_file(const google::protobuf::FileDescriptorProto &proto)
Add a protobuf file defined in a google::protobuf::FileDescriptorProto.
Helper class for creating google::protobuf::Message objects that are not statically compiled into the...
static GoogleProtobufMessagePointer new_protobuf_message(const google::protobuf::Descriptor *desc)
Create a new (empty) Google Protobuf message of a given type by Descriptor.
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.
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)
Dynamic Compact Control Language namespace.
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 add_database(boost::shared_ptr< google::protobuf::DescriptorDatabase > database)
Add a Google Protobuf DescriptorDatabase to the set of databases searched for Message Descriptors...
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 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.