DCCL v5
Loading...
Searching...
No Matches
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
35namespace 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 std::string(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
308template <class Codec>
309typename 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
313dccl::FieldCodecManagerLocal::add(const std::string& name)
314{
315 type_helper_.add<typename Codec::wire_type>();
316 add_single_type<Codec>(
317 __mangle_name(name, std::string(Codec::wire_type::descriptor()->full_name())),
318 google::protobuf::FieldDescriptor::TYPE_MESSAGE,
319 google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
320}
321
322template <class Codec>
323typename std::enable_if<
324 !(std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
325 !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value),
326 void>::type
327dccl::FieldCodecManagerLocal::add(const std::string& name)
328{
329 add_all_types<typename Codec::wire_type, typename Codec::field_type, Codec>(name);
330}
331
332template <class Codec, google::protobuf::FieldDescriptor::Type type>
333void dccl::FieldCodecManagerLocal::add(const std::string& name)
334{
335 add_single_type<Codec>(name, type, google::protobuf::FieldDescriptor::TypeToCppType(type));
336}
337
338template <typename WireType, typename FieldType, class Codec>
339void dccl::FieldCodecManagerLocal::add_all_types(const std::string& name)
340{
341 using google::protobuf::FieldDescriptor;
342 const FieldDescriptor::CppType cpp_field_type = internal::ToProtoCppType<FieldType>::as_enum();
343 const FieldDescriptor::CppType cpp_wire_type = internal::ToProtoCppType<WireType>::as_enum();
344
345 for (int i = 1, n = FieldDescriptor::MAX_TYPE; i <= n; ++i)
346 {
347 auto field_type = static_cast<FieldDescriptor::Type>(i);
348 if (FieldDescriptor::TypeToCppType(field_type) == cpp_field_type)
349 {
350 add_single_type<Codec>(name, field_type, cpp_wire_type);
351 }
352 }
353}
354
355template <class Codec>
356void dccl::FieldCodecManagerLocal::add_single_type(
357 const std::string& name, google::protobuf::FieldDescriptor::Type field_type,
358 google::protobuf::FieldDescriptor::CppType wire_type)
359{
360 std::shared_ptr<FieldCodecBase> new_field_codec(new Codec());
361 new_field_codec->set_name(name);
362 new_field_codec->set_field_type(field_type);
363 new_field_codec->set_wire_type(wire_type);
364 new_field_codec->set_manager(this);
365 using google::protobuf::FieldDescriptor;
366 if (!codecs_[field_type].count(name))
367 {
368 codecs_[field_type][name] = new_field_codec;
369 dccl::dlog.is(dccl::logger::DEBUG1) && dccl::dlog << "Adding codec " << *new_field_codec
370 << std::endl;
371 }
372 else
373 {
374 dccl::dlog.is(dccl::logger::DEBUG1) &&
375 dccl::dlog << "Trying to add: " << *new_field_codec
376 << ", but already have duplicate codec (For `name`/`field type` pair) "
377 << *(codecs_[field_type].find(name)->second) << std::endl;
378 }
379}
380
381template <class Codec>
382typename std::enable_if<
383 std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
384 !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value,
385 void>::type
387{
388 type_helper_.remove<typename Codec::wire_type>();
389 remove_single_type<Codec>(
390 __mangle_name(name, std::string(Codec::wire_type::descriptor()->full_name())),
391 google::protobuf::FieldDescriptor::TYPE_MESSAGE,
392 google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
393}
394
395template <class Codec>
396typename std::enable_if<
397 !(std::is_base_of<google::protobuf::Message, typename Codec::wire_type>::value &&
398 !std::is_same<google::protobuf::Message, typename Codec::wire_type>::value),
399 void>::type
400dccl::FieldCodecManagerLocal::remove(const std::string& name)
401{
402 remove_all_types<typename Codec::wire_type, typename Codec::field_type, Codec>(name);
403}
404
405template <class Codec, google::protobuf::FieldDescriptor::Type type>
406void dccl::FieldCodecManagerLocal::remove(const std::string& name)
407{
408 remove_single_type<Codec>(name, type, google::protobuf::FieldDescriptor::TypeToCppType(type));
409}
410
411template <typename WireType, typename FieldType, class Codec>
412void dccl::FieldCodecManagerLocal::remove_all_types(const std::string& name)
413{
414 using google::protobuf::FieldDescriptor;
415 const FieldDescriptor::CppType cpp_field_type = internal::ToProtoCppType<FieldType>::as_enum();
416 const FieldDescriptor::CppType cpp_wire_type = internal::ToProtoCppType<WireType>::as_enum();
417
418 for (int i = 1, n = FieldDescriptor::MAX_TYPE; i <= n; ++i)
419 {
420 auto field_type = static_cast<FieldDescriptor::Type>(i);
421 if (FieldDescriptor::TypeToCppType(field_type) == cpp_field_type)
422 {
423 remove_single_type<Codec>(name, field_type, cpp_wire_type);
424 }
425 }
426}
427
428template <class Codec>
429void dccl::FieldCodecManagerLocal::remove_single_type(
430 const std::string& name, google::protobuf::FieldDescriptor::Type field_type,
431 google::protobuf::FieldDescriptor::CppType wire_type)
432{
433 using google::protobuf::FieldDescriptor;
434 if (codecs_[field_type].count(name))
435 {
436 dccl::dlog.is(dccl::logger::DEBUG1) &&
437 dccl::dlog << "Removing codec " << *codecs_[field_type][name] << std::endl;
438 codecs_[field_type].erase(name);
439 }
440 else
441 {
442 std::shared_ptr<FieldCodecBase> new_field_codec(new Codec());
443 new_field_codec->set_name(name);
444 new_field_codec->set_field_type(field_type);
445 new_field_codec->set_wire_type(wire_type);
446 new_field_codec->set_manager(this);
447
448 dccl::dlog.is(dccl::logger::DEBUG1) && dccl::dlog
449 << "Trying to remove: " << *new_field_codec
450 << ", but no such codec exists" << std::endl;
451 }
452}
453
454#endif
The Dynamic CCL enCODer/DECoder. This is the main class you will use to load, encode and decode DCCL ...
Definition codec.h:61
A class for managing the various field codecs. Here you can add and remove field codecs....
std::enable_if< std::is_base_of< google::protobuf::Message, typenameCodec::wire_type >::value &&!std::is_same< google::protobuf::Message, typenameCodec::wire_type >::value, void >::type remove(const std::string &name)
Remove a new field codec (used for codecs operating on statically generated Protobuf messages,...
std::enable_if< std::is_base_of< google::protobuf::Message, typenameCodec::wire_type >::value &&!std::is_same< google::protobuf::Message, typenameCodec::wire_type >::value, void >::type add(const std::string &name)
Add a new field codec (used for codecs operating on statically generated Protobuf messages,...
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...
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.
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:191
Dynamic Compact Control Language namespace.
Definition any.h:28