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() { }
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
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...
static boost::shared_ptr< FieldCodecBase > find(const google::protobuf::Descriptor *desc, std::string name="")
Find the codec for a given base (or embedded) message.
The Dynamic CCL enCODer/DECoder. This is the main class you will use to load, encode and decode DCCL ...
Definition: codec.h:56
See Bug #1089061, and Boost MPL Documentation section 3.4 (compiler workarounds)
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...
A class for managing the various field codecs. Here you can add and remove field codecs. The DCCL Codec and DefaultMessageCodec use the find() methods to locate the appropriate field codec.
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...
Dynamic Compact Control Language namespace.
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