DCCL v4
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 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>(__mangle_name(name, Codec::wire_type::descriptor()->full_name()),
317 google::protobuf::FieldDescriptor::TYPE_MESSAGE,
318 google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE);
319}
320
321template <class Codec>
322typename 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
326dccl::FieldCodecManagerLocal::add(const std::string& name)
327{
328 add_all_types<typename Codec::wire_type, typename Codec::field_type, Codec>(name);
329}
330
331template <class Codec, google::protobuf::FieldDescriptor::Type type>
332void dccl::FieldCodecManagerLocal::add(const std::string& name)
333{
334 add_single_type<Codec>(name, type, google::protobuf::FieldDescriptor::TypeToCppType(type));
335}
336
337template <typename WireType, typename FieldType, class Codec>
338void 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
354template <class Codec>
355void 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
380template <class Codec>
381typename 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
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
393template <class Codec>
394typename 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
398dccl::FieldCodecManagerLocal::remove(const std::string& name)
399{
400 remove_all_types<typename Codec::wire_type, typename Codec::field_type, Codec>(name);
401}
402
403template <class Codec, google::protobuf::FieldDescriptor::Type type>
404void dccl::FieldCodecManagerLocal::remove(const std::string& name)
405{
406 remove_single_type<Codec>(name, type, google::protobuf::FieldDescriptor::TypeToCppType(type));
407}
408
409template <typename WireType, typename FieldType, class Codec>
410void 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
426template <class Codec>
427void 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
The Dynamic CCL enCODer/DECoder. This is the main class you will use to load, encode and decode DCCL ...
Definition codec.h:63
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:192
Dynamic Compact Control Language namespace.
Definition any.h:47