DCCL v4
field_codec_manager.h
1 // Copyright 2011-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 FieldCodecManager20110405H
25 #define FieldCodecManager20110405H
26 
27 #include <type_traits>
28 
29 #include "field_codec.h"
30 #include "internal/field_codec_data.h"
31 #include "internal/type_helper.h"
32 #include "logger.h"
33 #include "thread_safety.h"
34 
35 namespace dccl
36 {
37 
40 {
41  public:
44 
45  // field type == wire type
46  /* template<typename FieldType, template <typename FieldType> class Codec> */
47  /* void add(const std::string& name); */
48 
54  template <class Codec>
55  typename std::enable_if<
56  std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
57  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value,
58  void>::type
59  add(const std::string& name);
60 
66  template <class Codec>
67  typename std::enable_if<
68  !(std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
69  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value),
70  void>::type
71  add(const std::string& name);
72 
78  template <class Codec, google::protobuf::FieldDescriptor::Type type>
79  void add(const std::string& name);
80 
86  template <class Codec>
87  typename std::enable_if<
88  std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
89  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value,
90  void>::type
91  remove(const std::string& name);
92 
98  template <class Codec>
99  typename std::enable_if<
100  !(std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
101  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value),
102  void>::type
103  remove(const std::string& name);
104 
110  template <class Codec, google::protobuf::FieldDescriptor::Type type>
111  void remove(const std::string& name);
112 
114  std::shared_ptr<FieldCodecBase> find(const google::protobuf::FieldDescriptor* field,
115  int codec_version, bool has_codec_group,
116  const std::string& codec_group) const
117  {
118  std::string name = __find_codec(field, has_codec_group, codec_group);
119 
120  if (field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE)
121  return find(field->message_type(), codec_version, name);
122  else
123  return __find(field->type(), codec_version, name, "");
124  }
125 
130  std::shared_ptr<FieldCodecBase> find(const google::protobuf::Descriptor* desc,
131  int codec_version = 0, std::string name = "") const
132  {
133  // this was called on the root message
134  if (name.empty())
135  {
136  // explicitly declared codec takes precedence over group
137  if (desc->options().GetExtension(dccl::msg).has_codec())
138  name = desc->options().GetExtension(dccl::msg).codec();
139  else
140  name = FieldCodecBase::codec_group(desc);
141  codec_version = desc->options().GetExtension(dccl::msg).codec_version();
142  }
143 
144  return __find(google::protobuf::FieldDescriptor::TYPE_MESSAGE, codec_version, name,
145  desc->full_name());
146  }
147 
148  std::shared_ptr<FieldCodecBase> find(google::protobuf::FieldDescriptor::Type type,
149  int codec_version, std::string name) const
150  {
151  return __find(type, codec_version, name, "");
152  }
153 
154  void clear()
155  {
156  type_helper_.reset();
157  codecs_.clear();
158  }
159 
160  internal::TypeHelper& type_helper() { return type_helper_; }
161  const internal::TypeHelper& type_helper() const { return type_helper_; }
162 
163  internal::CodecData& codec_data() { return codec_data_; }
164  const internal::CodecData& codec_data() const { return codec_data_; }
165 
166  void set_hash(const google::protobuf::Descriptor* desc, std::size_t hash)
167  {
168  hashes_[desc] = hash;
169  }
170  bool has_hash(const google::protobuf::Descriptor* desc) const { return hashes_.count(desc); }
171  std::size_t hash(const google::protobuf::Descriptor* desc) const { return hashes_.at(desc); }
172 
173  private:
174  std::shared_ptr<FieldCodecBase> __find(google::protobuf::FieldDescriptor::Type type,
175  int codec_version, const std::string& codec_name,
176  const std::string& type_name) const;
177 
178  std::string __mangle_name(const std::string& codec_name, const std::string& type_name) const
179  {
180  return type_name.empty() ? codec_name : codec_name + "[" + type_name + "]";
181  }
182 
183  template <typename WireType, typename FieldType, class Codec>
184  void add_all_types(const std::string& name);
185 
186  template <class Codec>
187  void add_single_type(const std::string& name,
188  google::protobuf::FieldDescriptor::Type field_type,
189  google::protobuf::FieldDescriptor::CppType wire_type);
190 
191  void add_single_type(std::shared_ptr<FieldCodecBase> new_field_codec, const std::string& name,
192  google::protobuf::FieldDescriptor::Type field_type,
193  google::protobuf::FieldDescriptor::CppType wire_type);
194 
195  template <typename WireType, typename FieldType, class Codec>
196  void remove_all_types(const std::string& name);
197 
198  template <class Codec>
199  void remove_single_type(const std::string& name,
200  google::protobuf::FieldDescriptor::Type field_type,
201  google::protobuf::FieldDescriptor::CppType wire_type);
202 
203  std::string __find_codec(const google::protobuf::FieldDescriptor* field, bool has_codec_group,
204  const std::string& codec_group) const
205  {
206  dccl::DCCLFieldOptions dccl_field_options = field->options().GetExtension(dccl::field);
207 
208  // prefer the codec listed as a field extension
209  if (dccl_field_options.has_codec())
210  return dccl_field_options.codec();
211  // then, the codec embedded in the message option extension
212  else if (field->cpp_type() == google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE &&
213  field->message_type()->options().GetExtension(dccl::msg).has_codec())
214  return field->message_type()->options().GetExtension(dccl::msg).codec();
215  // then the overarching codec group
216  else if (has_codec_group)
217  return codec_group;
218  // finally the default
219  else
220  return dccl_field_options.codec();
221  }
222 
223  void check_deprecated(const std::string& name) const;
224 
225  private:
226  using InsideMap = std::map<std::string, std::shared_ptr<FieldCodecBase>>;
227  std::map<google::protobuf::FieldDescriptor::Type, InsideMap> codecs_;
228 
229  internal::TypeHelper type_helper_;
230  internal::CodecData codec_data_;
231 
232  std::map<const google::protobuf::Descriptor*, std::size_t> hashes_;
233  std::map<std::string, std::string> deprecated_names_;
234 };
235 
237 {
238  public:
239  template <class Codec>
240  typename std::enable_if<
241  std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
242  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value,
243  void>::type
244  add(const std::string& /*name*/)
245  {
246  static_assert(sizeof(Codec) == 0,
247  "The global `FieldCodecManager::add<...>(...)` is no longer available. Use "
248  "`dccl::Codec codec; codec.manager().add<...>(...)` instead, or for the ID "
249  "Codec use `dccl::Codec codec(\"id_codec_name\", MyIDCodec())`.");
250  }
251 
252  template <class Codec>
253  typename std::enable_if<
254  !(std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
255  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value),
256  void>::type
257  add(const std::string& /*name*/)
258  {
259  static_assert(sizeof(Codec) == 0,
260  "The global `FieldCodecManager::add<...>(...)` is no longer available. Use "
261  "`dccl::Codec codec; codec.manager().add<...>(...)` instead.`");
262  }
263 
264  template <class Codec, google::protobuf::FieldDescriptor::Type type>
265  static void add(const std::string& /*name*/)
266  {
267  static_assert(sizeof(Codec) == 0,
268  "The global `FieldCodecManager::add<...>(...)` is no longer available. Use "
269  "`dccl::Codec codec; codec.manager().add<...>(...)` instead, or for the ID "
270  "Codec use `dccl::Codec codec(\"id_codec_name\", MyIDCodec())`.");
271  }
272 
273  template <class Codec>
274  typename std::enable_if<
275  std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
276  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value,
277  void>::type
278  remove(const std::string& /*name*/)
279  {
280  static_assert(sizeof(Codec) == 0,
281  "The global FieldCodecManager::remove<...>(...) is no longer available. Use "
282  "`dccl::Codec codec; codec.manager().remove<...>(...) instead");
283  }
284 
285  template <class Codec>
286  typename std::enable_if<
287  !(std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
288  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value),
289  void>::type
290  remove(const std::string& /*name*/)
291  {
292  static_assert(sizeof(Codec) == 0,
293  "The global FieldCodecManager::remove<...>(...) is no longer available. Use "
294  "`dccl::Codec codec; codec.manager().remove<...>(...) instead");
295  }
296 
297  template <class Codec, google::protobuf::FieldDescriptor::Type type>
298  static void remove(const std::string& /*name*/)
299  {
300  static_assert(sizeof(Codec) == 0,
301  "The global FieldCodecManager::remove<...>(...) is no longer available. Use "
302  "`dccl::Codec codec; codec.manager().remove<...>(...) instead");
303  }
304 
305 }; // namespace dccl
306 } // namespace dccl
307 
308 template <class Codec>
309 typename std::enable_if<
310  std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
311  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value,
312  void>::type
313 dccl::FieldCodecManagerLocal::add(const std::string& name)
314 {
315  type_helper_.add<typename Codec::wire_type>();
316  add_single_type<Codec>(__mangle_name(name, Codec::wire_type::descriptor()->full_name()),
317  google::protobuf::FieldDescriptor::TYPE_MESSAGE,
318  google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
319 }
320 
321 template <class Codec>
322 typename std::enable_if<
323  !(std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
324  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value),
325  void>::type
326 dccl::FieldCodecManagerLocal::add(const std::string& name)
327 {
328  add_all_types<typename Codec::wire_type, typename Codec::field_type, Codec>(name);
329 }
330 
331 template <class Codec, google::protobuf::FieldDescriptor::Type type>
332 void dccl::FieldCodecManagerLocal::add(const std::string& name)
333 {
334  add_single_type<Codec>(name, type, google::protobuf::FieldDescriptor::TypeToCppType(type));
335 }
336 
337 template <typename WireType, typename FieldType, class Codec>
338 void dccl::FieldCodecManagerLocal::add_all_types(const std::string& name)
339 {
340  using google::protobuf::FieldDescriptor;
341  const FieldDescriptor::CppType cpp_field_type = internal::ToProtoCppType<FieldType>::as_enum();
342  const FieldDescriptor::CppType cpp_wire_type = internal::ToProtoCppType<WireType>::as_enum();
343 
344  for (int i = 1, n = FieldDescriptor::MAX_TYPE; i <= n; ++i)
345  {
346  auto field_type = static_cast<FieldDescriptor::Type>(i);
347  if (FieldDescriptor::TypeToCppType(field_type) == cpp_field_type)
348  {
349  add_single_type<Codec>(name, field_type, cpp_wire_type);
350  }
351  }
352 }
353 
354 template <class Codec>
355 void dccl::FieldCodecManagerLocal::add_single_type(
356  const std::string& name, google::protobuf::FieldDescriptor::Type field_type,
357  google::protobuf::FieldDescriptor::CppType wire_type)
358 {
359  std::shared_ptr<FieldCodecBase> new_field_codec(new Codec());
360  new_field_codec->set_name(name);
361  new_field_codec->set_field_type(field_type);
362  new_field_codec->set_wire_type(wire_type);
363  new_field_codec->set_manager(this);
364  using google::protobuf::FieldDescriptor;
365  if (!codecs_[field_type].count(name))
366  {
367  codecs_[field_type][name] = new_field_codec;
368  dccl::dlog.is(dccl::logger::DEBUG1) && dccl::dlog << "Adding codec " << *new_field_codec
369  << std::endl;
370  }
371  else
372  {
373  dccl::dlog.is(dccl::logger::DEBUG1) &&
374  dccl::dlog << "Trying to add: " << *new_field_codec
375  << ", but already have duplicate codec (For `name`/`field type` pair) "
376  << *(codecs_[field_type].find(name)->second) << std::endl;
377  }
378 }
379 
380 template <class Codec>
381 typename std::enable_if<
382  std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
383  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value,
384  void>::type
385 dccl::FieldCodecManagerLocal::remove(const std::string& name)
386 {
387  type_helper_.remove<typename Codec::wire_type>();
388  remove_single_type<Codec>(__mangle_name(name, Codec::wire_type::descriptor()->full_name()),
389  google::protobuf::FieldDescriptor::TYPE_MESSAGE,
390  google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
391 }
392 
393 template <class Codec>
394 typename std::enable_if<
395  !(std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
396  !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value),
397  void>::type
398 dccl::FieldCodecManagerLocal::remove(const std::string& name)
399 {
400  remove_all_types<typename Codec::wire_type, typename Codec::field_type, Codec>(name);
401 }
402 
403 template <class Codec, google::protobuf::FieldDescriptor::Type type>
404 void dccl::FieldCodecManagerLocal::remove(const std::string& name)
405 {
406  remove_single_type<Codec>(name, type, google::protobuf::FieldDescriptor::TypeToCppType(type));
407 }
408 
409 template <typename WireType, typename FieldType, class Codec>
410 void dccl::FieldCodecManagerLocal::remove_all_types(const std::string& name)
411 {
412  using google::protobuf::FieldDescriptor;
413  const FieldDescriptor::CppType cpp_field_type = internal::ToProtoCppType<FieldType>::as_enum();
414  const FieldDescriptor::CppType cpp_wire_type = internal::ToProtoCppType<WireType>::as_enum();
415 
416  for (int i = 1, n = FieldDescriptor::MAX_TYPE; i <= n; ++i)
417  {
418  auto field_type = static_cast<FieldDescriptor::Type>(i);
419  if (FieldDescriptor::TypeToCppType(field_type) == cpp_field_type)
420  {
421  remove_single_type<Codec>(name, field_type, cpp_wire_type);
422  }
423  }
424 }
425 
426 template <class Codec>
427 void dccl::FieldCodecManagerLocal::remove_single_type(
428  const std::string& name, google::protobuf::FieldDescriptor::Type field_type,
429  google::protobuf::FieldDescriptor::CppType wire_type)
430 {
431  using google::protobuf::FieldDescriptor;
432  if (codecs_[field_type].count(name))
433  {
434  dccl::dlog.is(dccl::logger::DEBUG1) &&
435  dccl::dlog << "Removing codec " << *codecs_[field_type][name] << std::endl;
436  codecs_[field_type].erase(name);
437  }
438  else
439  {
440  std::shared_ptr<FieldCodecBase> new_field_codec(new Codec());
441  new_field_codec->set_name(name);
442  new_field_codec->set_field_type(field_type);
443  new_field_codec->set_wire_type(wire_type);
444  new_field_codec->set_manager(this);
445 
446  dccl::dlog.is(dccl::logger::DEBUG1) && dccl::dlog
447  << "Trying to remove: " << *new_field_codec
448  << ", but no such codec exists" << std::endl;
449  }
450 }
451 
452 #endif
dccl::FieldCodecManagerLocal
A class for managing the various field codecs. Here you can add and remove field codecs....
Definition: field_codec_manager.h:39
dccl::DCCLFieldOptions
Definition: option_extensions.pb.h:476
dccl::FieldCodecManagerLocal::find
std::shared_ptr< FieldCodecBase > find(const google::protobuf::FieldDescriptor *field, int codec_version, bool has_codec_group, const std::string &codec_group) const
Find the codec for a given field. For embedded messages, prefers (dccl.field).codec (inside field) ov...
Definition: field_codec_manager.h:114
dccl
Dynamic Compact Control Language namespace.
Definition: any.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:192
dccl::Codec
The Dynamic CCL enCODer/DECoder. This is the main class you will use to load, encode and decode DCCL ...
Definition: codec.h:62
dccl::FieldCodecManagerLocal::find
std::shared_ptr< FieldCodecBase > find(const google::protobuf::Descriptor *desc, int codec_version=0, std::string name="") const
Find the codec for a given base (or embedded) message.
Definition: field_codec_manager.h:130
dccl::FieldCodecManagerLocal::add
std::enable_if< std::is_base_of< google::protobuf::Message, typename Codec::wire_type >::value &&!std::is_same< google::protobuf::Message, typename Codec::wire_type >::value, void >::type add(const std::string &name)
Add a new field codec (used for codecs operating on statically generated Protobuf messages,...
Definition: field_codec_manager.h:313
dccl::FieldCodecManager
Definition: field_codec_manager.h:236
dccl::internal::ToProtoCppType
Definition: protobuf_cpp_type_helpers.h:543
dccl::FieldCodecManagerLocal::remove
std::enable_if< std::is_base_of< google::protobuf::Message, typename Codec::wire_type >::value &&!std::is_same< google::protobuf::Message, typename Codec::wire_type >::value, void >::type remove(const std::string &name)
Remove a new field codec (used for codecs operating on statically generated Protobuf messages,...
Definition: field_codec_manager.h:385