DCCL v3
field_codec_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 FieldCodecManager20110405H
23 #define FieldCodecManager20110405H
24 
25 #include <boost/utility/enable_if.hpp>
26 #include <boost/type_traits.hpp>
27 #include <boost/mpl/and.hpp>
28 #include <boost/mpl/not.hpp>
29 #include <boost/mpl/logical.hpp>
30 
31 #include "internal/type_helper.h"
32 #include "field_codec.h"
33 #include "dccl/logger.h"
34 
35 namespace dccl
36 {
37  namespace compiler
38  {
40  template <int> struct dummy_fcm { dummy_fcm(int) {} };
41  }
42 
45  {
46  public:
47  // field type == wire type
48  /* template<typename FieldType, template <typename FieldType> class Codec> */
49  /* static void add(const std::string& name); */
50 
56  template<class Codec>
57  typename boost::enable_if<
58  boost::mpl::and_<boost::is_base_of<google::protobuf::Message, typename Codec::wire_type>,
59  boost::mpl::not_<boost::is_same<google::protobuf::Message, typename Codec::wire_type> >
60  >,
61  void>::type
62  static add(const std::string& name, compiler::dummy_fcm<0> dummy_fcm = 0);
63 
69  template<class Codec>
70  typename boost::disable_if<
71  boost::mpl::and_<boost::is_base_of<google::protobuf::Message, typename Codec::wire_type>,
72  boost::mpl::not_<boost::is_same<google::protobuf::Message,typename Codec::wire_type> >
73  >,
74  void>::type
75  static add(const std::string& name, compiler::dummy_fcm<1> dummy_fcm = 0);
76 
82  template<class Codec, google::protobuf::FieldDescriptor::Type type>
83  static void add(const std::string& name);
84 
90  template<class Codec>
91  typename boost::enable_if<
92  boost::mpl::and_<boost::is_base_of<google::protobuf::Message, typename Codec::wire_type>,
93  boost::mpl::not_<boost::is_same<google::protobuf::Message, typename Codec::wire_type> >
94  >,
95  void>::type
96  static remove(const std::string& name, compiler::dummy_fcm<0> dummy_fcm = 0);
97 
103  template<class Codec>
104  typename boost::disable_if<
105  boost::mpl::and_<boost::is_base_of<google::protobuf::Message, typename Codec::wire_type>,
106  boost::mpl::not_<boost::is_same<google::protobuf::Message,typename Codec::wire_type> >
107  >,
108  void>::type
109  static remove(const std::string& name, compiler::dummy_fcm<1> dummy_fcm = 0);
110 
116  template<class Codec, google::protobuf::FieldDescriptor::Type type>
117  static void remove(const std::string& name);
118 
119 
121  static boost::shared_ptr<FieldCodecBase> find(
122  const google::protobuf::FieldDescriptor* field,
123  bool has_codec_group,
124  const std::string& codec_group)
125  {
126  std::string name = __find_codec(field, has_codec_group, codec_group);
127 
128  if(field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE)
129  return find(field->message_type(), name);
130  else
131  return __find(field->type(), name);
132  }
133 
138  static boost::shared_ptr<FieldCodecBase> find(
139  const google::protobuf::Descriptor* desc,
140  std::string name = "")
141  {
142  // this was called on the root message
143  if(name.empty())
144  {
145  // explicitly declared codec takes precedence over group
146  if(desc->options().GetExtension(dccl::msg).has_codec())
147  name = desc->options().GetExtension(dccl::msg).codec();
148  else
149  name = FieldCodecBase::codec_group(desc);
150  }
151 
152  return __find(google::protobuf::FieldDescriptor::TYPE_MESSAGE,
153  name, desc->full_name());
154  }
155 
156  static boost::shared_ptr<FieldCodecBase> find(
157  google::protobuf::FieldDescriptor::Type type,
158  std::string name)
159  {
160  return __find(type, name);
161  }
162 
163  static void clear()
164  {
165  internal::TypeHelper::reset();
166  codecs_.clear();
167  }
168 
169 
170  private:
171  FieldCodecManager() { }
172  ~FieldCodecManager() { }
173  FieldCodecManager(const FieldCodecManager&);
174  FieldCodecManager& operator= (const FieldCodecManager&);
175 
176 
177 
178  static boost::shared_ptr<FieldCodecBase> __find(
179  google::protobuf::FieldDescriptor::Type type,
180  const std::string& codec_name,
181  const std::string& type_name = "");
182 
183  static std::string __mangle_name(const std::string& codec_name,
184  const std::string& type_name)
185  { return type_name.empty() ? codec_name : codec_name + "[" + type_name + "]"; }
186 
187 
188  template<typename WireType, typename FieldType, class Codec>
189  static void add_all_types(const std::string& name);
190 
191  template<class Codec>
192  static void add_single_type(const std::string& name,
193  google::protobuf::FieldDescriptor::Type field_type,
194  google::protobuf::FieldDescriptor::CppType wire_type);
195 
196  template<typename WireType, typename FieldType, class Codec>
197  static void remove_all_types(const std::string& name);
198 
199  template<class Codec>
200  static void remove_single_type(const std::string& name,
201  google::protobuf::FieldDescriptor::Type field_type,
202  google::protobuf::FieldDescriptor::CppType wire_type);
203 
204 
205  static std::string __find_codec(const google::protobuf::FieldDescriptor* field,
206  bool has_codec_group, const std::string& codec_group)
207  {
208  dccl::DCCLFieldOptions dccl_field_options = field->options().GetExtension(dccl::field);
209 
210  // prefer the codec listed as a field extension
211  if(dccl_field_options.has_codec())
212  return dccl_field_options.codec();
213  // then, the codec embedded in the message option extension
214  else if(field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE && field->message_type()->options().GetExtension(dccl::msg).has_codec())
215  return field->message_type()->options().GetExtension(dccl::msg).codec();
216  // then the overarching codec group
217  else if(has_codec_group)
218  return codec_group;
219  // finally the default
220  else
221  return dccl_field_options.codec();
222  }
223 
224  private:
225  typedef std::map<std::string, boost::shared_ptr<FieldCodecBase> > InsideMap;
226  static std::map<google::protobuf::FieldDescriptor::Type, InsideMap> codecs_;
227  };
228 }
229 
230 template<class Codec>
231 typename boost::enable_if<
232 boost::mpl::and_<
233 boost::is_base_of<google::protobuf::Message, typename Codec::wire_type>,
234 boost::mpl::not_<boost::is_same<google::protobuf::Message, typename Codec::wire_type> >
235 >,
236 void>::type
237  dccl::FieldCodecManager::add(const std::string& name, compiler::dummy_fcm<0> dummy_fcm)
238 {
239  internal::TypeHelper::add<typename Codec::wire_type>();
240  add_single_type<Codec>(__mangle_name(name, Codec::wire_type::descriptor()->full_name()),
241  google::protobuf::FieldDescriptor::TYPE_MESSAGE,
242  google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
243 }
244 
245 template<class Codec>
246 typename boost::disable_if<
247 boost::mpl::and_<
248 boost::is_base_of<google::protobuf::Message, typename Codec::wire_type>,
249  boost::mpl::not_<boost::is_same<google::protobuf::Message, typename Codec::wire_type> >
250  >,
251  void>::type
252  dccl::FieldCodecManager::add(const std::string& name, compiler::dummy_fcm<1> dummy_fcm)
253 {
254  add_all_types<typename Codec::wire_type, typename Codec::field_type, Codec>(name);
255 }
256 
257 template<class Codec, google::protobuf::FieldDescriptor::Type type>
258  void dccl::FieldCodecManager::add(const std::string& name)
259 {
260  add_single_type<Codec>(name, type, google::protobuf::FieldDescriptor::TypeToCppType(type));
261 }
262 
263 
264 template<typename WireType, typename FieldType, class Codec>
265  void dccl::FieldCodecManager::add_all_types(const std::string& name)
266 {
267  using google::protobuf::FieldDescriptor;
268  const FieldDescriptor::CppType cpp_field_type = internal::ToProtoCppType<FieldType>::as_enum();
269  const FieldDescriptor::CppType cpp_wire_type = internal::ToProtoCppType<WireType>::as_enum();
270 
271  for(int i = 1, n = FieldDescriptor::MAX_TYPE; i <= n; ++i)
272  {
273  FieldDescriptor::Type field_type = static_cast<FieldDescriptor::Type>(i);
274  if(FieldDescriptor::TypeToCppType(field_type) == cpp_field_type)
275  {
276  add_single_type<Codec>(name, field_type, cpp_wire_type);
277  }
278  }
279 }
280 
281 template<class Codec>
282 void dccl::FieldCodecManager::add_single_type(const std::string& name,
283  google::protobuf::FieldDescriptor::Type field_type,
284  google::protobuf::FieldDescriptor::CppType wire_type)
285 {
286  using google::protobuf::FieldDescriptor;
287  if(!codecs_[field_type].count(name))
288  {
289  boost::shared_ptr<FieldCodecBase> new_field_codec(new Codec());
290  new_field_codec->set_name(name);
291  new_field_codec->set_field_type(field_type);
292  new_field_codec->set_wire_type(wire_type);
293 
294  codecs_[field_type][name] = new_field_codec;
295  dccl::dlog.is(dccl::logger::DEBUG1) && dccl::dlog << "Adding codec " << *new_field_codec << std::endl;
296  }
297  else
298  {
299  boost::shared_ptr<FieldCodecBase> new_field_codec(new Codec());
300  new_field_codec->set_name(name);
301  new_field_codec->set_field_type(field_type);
302  new_field_codec->set_wire_type(wire_type);
303 
304  dccl::dlog.is(dccl::logger::DEBUG1) && dccl::dlog << "Trying to add: " << *new_field_codec
305  << ", but already have duplicate codec (For `name`/`field type` pair) "
306  << *(codecs_[field_type].find(name)->second)
307  << std::endl;
308  }
309 }
310 
311 
312 
313 template<class Codec>
314 typename boost::enable_if<
315 boost::mpl::and_<
316 boost::is_base_of<google::protobuf::Message, typename Codec::wire_type>,
317 boost::mpl::not_<boost::is_same<google::protobuf::Message, typename Codec::wire_type> >
318 >,
319 void>::type
320  dccl::FieldCodecManager::remove(const std::string& name, compiler::dummy_fcm<0> dummy_fcm)
321 {
322  internal::TypeHelper::remove<typename Codec::wire_type>();
323  remove_single_type<Codec>(__mangle_name(name, Codec::wire_type::descriptor()->full_name()),
324  google::protobuf::FieldDescriptor::TYPE_MESSAGE,
325  google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
326 }
327 
328 template<class Codec>
329 typename boost::disable_if<
330 boost::mpl::and_<
331 boost::is_base_of<google::protobuf::Message, typename Codec::wire_type>,
332  boost::mpl::not_<boost::is_same<google::protobuf::Message, typename Codec::wire_type> >
333  >,
334  void>::type
335  dccl::FieldCodecManager::remove(const std::string& name, compiler::dummy_fcm<1> dummy_fcm)
336 {
337  remove_all_types<typename Codec::wire_type, typename Codec::field_type, Codec>(name);
338 }
339 
340 template<class Codec, google::protobuf::FieldDescriptor::Type type>
341  void dccl::FieldCodecManager::remove(const std::string& name)
342 {
343  remove_single_type<Codec>(name, type, google::protobuf::FieldDescriptor::TypeToCppType(type));
344 }
345 
346 
347 template<typename WireType, typename FieldType, class Codec>
348  void dccl::FieldCodecManager::remove_all_types(const std::string& name)
349 {
350  using google::protobuf::FieldDescriptor;
351  const FieldDescriptor::CppType cpp_field_type = internal::ToProtoCppType<FieldType>::as_enum();
352  const FieldDescriptor::CppType cpp_wire_type = internal::ToProtoCppType<WireType>::as_enum();
353 
354  for(int i = 1, n = FieldDescriptor::MAX_TYPE; i <= n; ++i)
355  {
356  FieldDescriptor::Type field_type = static_cast<FieldDescriptor::Type>(i);
357  if(FieldDescriptor::TypeToCppType(field_type) == cpp_field_type)
358  {
359  remove_single_type<Codec>(name, field_type, cpp_wire_type);
360  }
361  }
362 }
363 
364 template<class Codec>
365 void dccl::FieldCodecManager::remove_single_type(const std::string& name,
366  google::protobuf::FieldDescriptor::Type field_type,
367  google::protobuf::FieldDescriptor::CppType wire_type)
368 {
369  using google::protobuf::FieldDescriptor;
370  if(codecs_[field_type].count(name))
371  {
372  dccl::dlog.is(dccl::logger::DEBUG1) && dccl::dlog << "Removing codec " << *codecs_[field_type][name] << std::endl;
373  codecs_[field_type].erase(name);
374  }
375  else
376  {
377  boost::shared_ptr<FieldCodecBase> new_field_codec(new Codec());
378  new_field_codec->set_name(name);
379  new_field_codec->set_field_type(field_type);
380  new_field_codec->set_wire_type(wire_type);
381 
382  dccl::dlog.is(dccl::logger::DEBUG1) && dccl::dlog << "Trying to remove: " << *new_field_codec
383  << ", but no such codec exists"
384  << std::endl;
385  }
386 }
387 
388 
389 
390 
391 #endif
dccl::DCCLFieldOptions
Definition: option_extensions.pb.h:278
dccl
Dynamic Compact Control Language namespace.
Definition: gen_units_class_plugin.h:49
dccl::Logger::is
bool is(logger::Verbosity verbosity, logger::Group group=logger::GENERAL)
Indicates the verbosity of the Logger until the next std::flush or std::endl. The boolean return is u...
Definition: logger.h:145
dccl::FieldCodecManager::remove
static boost::enable_if< boost::mpl::and_< boost::is_base_of< google::protobuf::Message, typename Codec::wire_type >, boost::mpl::not_< boost::is_same< google::protobuf::Message, typename Codec::wire_type > > >, void >::type remove(const std::string &name, compiler::dummy_fcm< 0 > dummy_fcm=0)
Remove a new field codec (used for codecs operating on statically generated Protobuf messages,...
dccl::FieldCodecManager::find
static boost::shared_ptr< FieldCodecBase > find(const google::protobuf::Descriptor *desc, std::string name="")
Find the codec for a given base (or embedded) message.
Definition: field_codec_manager.h:138
dccl::compiler::dummy_fcm
See Bug #1089061, and Boost MPL Documentation section 3.4 (compiler workarounds)
Definition: field_codec_manager.h:40
dccl::FieldCodecManager::add
static boost::enable_if< boost::mpl::and_< boost::is_base_of< google::protobuf::Message, typename Codec::wire_type >, boost::mpl::not_< boost::is_same< google::protobuf::Message, typename Codec::wire_type > > >, void >::type add(const std::string &name, compiler::dummy_fcm< 0 > dummy_fcm=0)
Add a new field codec (used for codecs operating on statically generated Protobuf messages,...
dccl::FieldCodecManager::find
static boost::shared_ptr< FieldCodecBase > find(const google::protobuf::FieldDescriptor *field, bool has_codec_group, const std::string &codec_group)
Find the codec for a given field. For embedded messages, prefers (dccl.field).codec (inside field) ov...
Definition: field_codec_manager.h:121
dccl::FieldCodecManager
A class for managing the various field codecs. Here you can add and remove field codecs....
Definition: field_codec_manager.h:44
dccl::internal::ToProtoCppType
Definition: protobuf_cpp_type_helpers.h:512