Ice 3.9
C++ API Reference
Loading...
Searching...
No Matches
InternalT.h
1// Copyright (c) ZeroC, Inc.
2
3#pragma once
4
5#include "Config.h"
6#include "Ice/Demangle.h"
7#include "Ice/Ice.h"
8#include "InternalI.h"
9#include "Types.h"
10
11#if defined(__clang__)
12# pragma clang diagnostic push
13# pragma clang diagnostic ignored "-Wshadow-field-in-constructor"
14#elif defined(__GNUC__)
15# pragma GCC diagnostic push
16# pragma GCC diagnostic ignored "-Wshadow"
17#endif
18
19namespace DataStorm
20{
21 template<typename K, typename V, typename U> class Sample;
22}
23
24namespace DataStormI
25{
26 template<typename T> class has_communicator_parameter
27 {
28 template<typename TT, typename SS>
29 static auto testE(int) noexcept
30 -> decltype(TT::encode(std::declval<Ice::CommunicatorPtr&>(), std::declval<SS&>()), std::true_type());
31
32 template<typename, typename> static auto testE(...) -> std::false_type;
33
34 template<typename TT, typename SS>
35 static auto testD(int) noexcept
36 -> decltype(TT::decode(std::declval<Ice::CommunicatorPtr&>(), Ice::ByteSeq()), std::true_type());
37
38 template<typename, typename> static auto testD(...) -> std::false_type;
39
40 public:
41 static constexpr bool value =
42 decltype(testE<DataStorm::Encoder<T>, T>(0))::value && decltype(testD<DataStorm::Decoder<T>, T>(0))::value;
43 };
44
45 template<typename T, typename Enabler = void> struct EncoderT
46 {
47 static Ice::ByteSeq encode(const Ice::CommunicatorPtr&, const T& value)
48 {
50 }
51 };
52
53 template<typename T, typename Enabler = void> struct DecoderT
54 {
55 static T decode(const Ice::CommunicatorPtr&, const Ice::ByteSeq& data)
56 {
58 }
59 };
60
61 template<typename T> struct EncoderT<T, std::enable_if_t<has_communicator_parameter<T>::value>>
62 {
63 static Ice::ByteSeq encode(const Ice::CommunicatorPtr& communicator, const T& value)
64 {
65 return DataStorm::Encoder<T>::encode(communicator, value);
66 }
67 };
68
69 template<typename T> struct DecoderT<T, std::enable_if_t<has_communicator_parameter<T>::value>>
70 {
71 static T decode(const Ice::CommunicatorPtr& communicator, const Ice::ByteSeq& data)
72 {
73 return DataStorm::Decoder<T>::decode(communicator, data);
74 }
75 };
76
77 template<typename T> class is_streamable
78 {
79 template<typename TT, typename SS>
80 static auto test(int) noexcept -> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type());
81
82 template<typename, typename> static auto test(...) noexcept -> std::false_type;
83
84 public:
85 static constexpr bool value = decltype(test<T, std::ostream>(0))::value;
86 };
87
88 template<typename T, typename Enabler = void> struct Stringifier
89 {
90 static std::string toString(const T& value)
91 {
92 std::ostringstream os;
93 os << IceInternal::demangle(typeid(value).name()) << '(' << &value << ')';
94 return os.str();
95 }
96 };
97
98 template<typename T> struct Stringifier<T, std::enable_if_t<is_streamable<T>::value>>
99 {
100 static std::string toString(const T& value)
101 {
102 std::ostringstream os;
103 os << value;
104 return os.str();
105 }
106 };
107
108 template<typename T> class AbstractElementT : public virtual Element
109 {
110 public:
111 template<typename TT> AbstractElementT(TT&& v, std::int64_t id) : _value(std::forward<TT>(v)), _id(id) {}
112
113 [[nodiscard]] std::string toString() const override
114 {
115 std::ostringstream os;
116 os << _id << ':' << Stringifier<T>::toString(_value);
117 return os.str();
118 }
119
120 [[nodiscard]] Ice::ByteSeq encode(const Ice::CommunicatorPtr& communicator) const override
121 {
122 return EncoderT<T>::encode(communicator, _value);
123 }
124
125 [[nodiscard]] std::int64_t getId() const override { return _id; }
126
127 [[nodiscard]] const T& get() const { return _value; }
128
129 protected:
130 const T _value;
131 const std::int64_t _id;
132 };
133
134 template<typename K, typename V>
135 class AbstractFactoryT : public std::enable_shared_from_this<AbstractFactoryT<K, V>>
136 {
137 /// A custom deleter to remove the element from the factory when the shared_ptr is deleted.
138 /// The deleter is used by elements created by the factory.
139 struct Deleter
140 {
141 void operator()(V* obj)
142 {
143 if (auto factory = _factory.lock())
144 {
145 factory->remove(obj);
146 }
147 delete obj;
148 }
149
150 std::weak_ptr<AbstractFactoryT<K, V>> _factory;
151
152 } _deleter;
153
154 public:
155 AbstractFactoryT() = default;
156
157 void init() { _deleter = Deleter{std::enable_shared_from_this<AbstractFactoryT<K, V>>::shared_from_this()}; }
158
159 template<typename F, typename... Args>
160 [[nodiscard]] std::shared_ptr<typename V::BaseClassType> create(F&& value, Args&&... args)
161 {
162 std::lock_guard<std::mutex> lock(_mutex);
163 return createImpl(std::forward<F>(value), std::forward<Args>(args)...);
164 }
165
166 [[nodiscard]] std::vector<std::shared_ptr<typename V::BaseClassType>> create(std::vector<K> values)
167 {
168 std::lock_guard<std::mutex> lock(_mutex);
169 std::vector<std::shared_ptr<typename V::BaseClassType>> seq;
170 seq.reserve(values.size());
171 for (auto& v : values)
172 {
173 seq.push_back(createImpl(std::move(v)));
174 }
175 return seq;
176 }
177
178 protected:
179 friend struct Deleter;
180
181 [[nodiscard]] std::shared_ptr<typename V::BaseClassType> getImpl(std::int64_t id) const
182 {
183 std::lock_guard<std::mutex> lock(_mutex);
184 auto p = _elementsById.find(id);
185 if (p != _elementsById.end())
186 {
187 return p->second.lock();
188 }
189 return nullptr;
190 }
191
192 template<typename F, typename... Args> [[nodiscard]] std::shared_ptr<V> createImpl(F&& value, Args&&... args)
193 {
194 // Called with _mutex locked
195
196 auto p = _elements.find(value);
197 if (p != _elements.end())
198 {
199 auto k = p->second.lock();
200 if (k)
201 {
202 return k;
203 }
204
205 // The key is being removed concurrently by the deleter, remove it now to allow the insertion of a new
206 // key. The deleter won't remove the new key.
207 _elements.erase(p);
208 }
209
210 auto k =
211 std::shared_ptr<V>(new V(std::forward<F>(value), std::forward<Args>(args)..., ++_nextId), _deleter);
212 _elements[k->get()] = k;
213 _elementsById[k->getId()] = k;
214 return k;
215 }
216
217 void remove(V* v)
218 {
219 // Make sure to declare the variable outside the synchronization in case the element needs
220 // to be deleted if it's not the same.
221 std::shared_ptr<V> e;
222 std::lock_guard<std::mutex> lock(_mutex);
223 auto p = _elements.find(v->get());
224 if (p != _elements.end())
225 {
226 // remove() runs from the element's deleter, so the entry for v is always expired; erase it unless a
227 // concurrent createImpl already replaced it with a new, still live element for the same value.
228 e = p->second.lock();
229 if (!e)
230 {
231 _elements.erase(p);
232 }
233 }
234 _elementsById.erase(v->getId());
235 }
236
237 mutable std::mutex _mutex;
238 std::map<K, std::weak_ptr<V>> _elements;
239 std::map<std::int64_t, std::weak_ptr<V>> _elementsById;
240 std::int64_t _nextId{1};
241 };
242
243 template<typename K> class KeyT final : public Key, public AbstractElementT<K>
244 {
245 public:
246 [[nodiscard]] std::string toString() const final { return "k" + AbstractElementT<K>::toString(); }
247
248 using AbstractElementT<K>::AbstractElementT;
249 using BaseClassType = Key;
250 };
251
252 template<typename K> class KeyFactoryT final : public KeyFactory, public AbstractFactoryT<K, KeyT<K>>
253 {
254 public:
255 using AbstractFactoryT<K, KeyT<K>>::AbstractFactoryT;
256
257 [[nodiscard]] std::shared_ptr<Key> get(std::int64_t id) const final
258 {
259 return AbstractFactoryT<K, KeyT<K>>::getImpl(id);
260 }
261
262 [[nodiscard]] std::shared_ptr<Key>
263 decode(const Ice::CommunicatorPtr& communicator, const Ice::ByteSeq& data) final
264 {
265 return AbstractFactoryT<K, KeyT<K>>::create(DecoderT<K>::decode(communicator, data));
266 }
267
268 [[nodiscard]] static std::shared_ptr<KeyFactoryT<K>> createFactory()
269 {
270 auto f = std::make_shared<KeyFactoryT<K>>();
271 f->init();
272 return f;
273 }
274 };
275
276 template<typename T> class TagT final : public Tag, public AbstractElementT<T>
277 {
278 public:
279 [[nodiscard]] std::string toString() const final { return "t" + AbstractElementT<T>::toString(); }
280
281 using AbstractElementT<T>::AbstractElementT;
282 using BaseClassType = Tag;
283 };
284
285 template<typename T> class TagFactoryT final : public TagFactory, public AbstractFactoryT<T, TagT<T>>
286 {
287 public:
288 using AbstractFactoryT<T, TagT<T>>::AbstractFactoryT;
289
290 [[nodiscard]] std::shared_ptr<Tag> get(std::int64_t id) const final
291 {
292 return AbstractFactoryT<T, TagT<T>>::getImpl(id);
293 }
294
295 [[nodiscard]] std::shared_ptr<Tag>
296 decode(const Ice::CommunicatorPtr& communicator, const Ice::ByteSeq& data) final
297 {
298 return AbstractFactoryT<T, TagT<T>>::create(DecoderT<T>::decode(communicator, data));
299 }
300
301 [[nodiscard]] static std::shared_ptr<TagFactoryT<T>> createFactory()
302 {
303 auto f = std::make_shared<TagFactoryT<T>>();
304 f->init();
305 return f;
306 }
307 };
308
309 template<typename Key, typename Value, typename UpdateTag>
310 class SampleT final : public Sample, public std::enable_shared_from_this<SampleT<Key, Value, UpdateTag>>
311 {
312 public:
313 SampleT(
314 std::string session,
315 std::string origin,
316 std::int64_t id,
318 const std::shared_ptr<DataStormI::Key>& key,
319 const std::shared_ptr<DataStormI::Tag>& tag,
320 Ice::ByteSeq value,
321 std::int64_t timestamp)
322 : Sample(std::move(session), std::move(origin), id, event, key, tag, std::move(value), timestamp),
323 _hasValue(false)
324 {
325 }
326
327 SampleT(DataStorm::SampleEvent event) : Sample(event), _hasValue(false) {}
328
329 SampleT(DataStorm::SampleEvent event, Value value) : Sample(event), _hasValue(true), _value(std::move(value)) {}
330
331 SampleT(Ice::ByteSeq value, const std::shared_ptr<Tag>& tag)
332 : Sample(DataStorm::SampleEvent::PartialUpdate, tag),
333 _hasValue(false)
334 {
335 _encodedValue = std::move(value);
336 }
337
338 [[nodiscard]] DataStorm::Sample<Key, Value, UpdateTag> get()
339 {
340 auto impl = std::enable_shared_from_this<SampleT<Key, Value, UpdateTag>>::shared_from_this();
341 return DataStorm::Sample<Key, Value, UpdateTag>(impl);
342 }
343
344 [[nodiscard]] const Key& getKey()
345 {
346 assert(key);
347 return std::static_pointer_cast<KeyT<Key>>(key)->get();
348 }
349
350 [[nodiscard]] const Value& getValue() const { return _value; }
351
352 [[nodiscard]] UpdateTag getTag() const
353 {
354 return tag ? std::static_pointer_cast<TagT<UpdateTag>>(tag)->get() : UpdateTag();
355 }
356
357 void setValue(Value value)
358 {
359 _value = std::move(value);
360 _hasValue = true;
361 }
362
363 [[nodiscard]] bool hasValue() const final { return _hasValue; }
364
365 void setValue(const std::shared_ptr<Sample>& sample) final
366 {
367 if (sample)
368 {
370 std::static_pointer_cast<DataStormI::SampleT<Key, Value, UpdateTag>>(sample)->getValue());
371 }
372 else
373 {
374 _value = Value{};
375 }
376 _hasValue = true;
377 }
378
379 [[nodiscard]] const Ice::ByteSeq& encode(const Ice::CommunicatorPtr& communicator) final
380 {
381 if (_encodedValue.empty())
382 {
383 _encodedValue = encodeValue(communicator);
384 }
385 return _encodedValue;
386 }
387
388 [[nodiscard]] Ice::ByteSeq encodeValue(const Ice::CommunicatorPtr& communicator) final
389 {
390 // A remove sample carries no value.
392 {
393 return {};
394 }
395
396 assert(_hasValue);
397 return EncoderT<Value>::encode(communicator, _value);
398 }
399
400 void decode(const Ice::CommunicatorPtr& communicator) final
401 {
402 // A remove sample carries no value.
404 {
405 return;
406 }
407
408 // A custom Encoder may encode a value to zero bytes; the Decoder defines the meaning of empty input.
409 _value = DecoderT<Value>::decode(communicator, _encodedValue);
410 _hasValue = true;
411 _encodedValue.clear();
412 }
413
414 private:
415 bool _hasValue;
416 // Value-initialized because getValue() returns this member for a value-less sample, where it is documented to
417 // return a default value; a scalar type would otherwise be indeterminate.
418 Value _value{};
419 };
420
421 template<typename Key, typename Value, typename UpdateTag> class SampleFactoryT final : public SampleFactory
422 {
423 public:
424 [[nodiscard]] std::shared_ptr<Sample> create(
425 std::string session,
426 std::string origin,
427 std::int64_t id,
429 const std::shared_ptr<DataStormI::Key>& key,
430 const std::shared_ptr<DataStormI::Tag>& tag,
431 Ice::ByteSeq value,
432 std::int64_t timestamp) final
433 {
434 return std::make_shared<SampleT<Key, Value, UpdateTag>>(
435 std::move(session),
436 std::move(origin),
437 id,
438 type,
439 key,
440 tag,
441 std::move(value),
442 timestamp);
443 }
444 };
445
446 template<typename C, typename V> class FilterT final : public Filter, public AbstractElementT<C>
447 {
448 public:
449 template<typename CC, typename FF>
450 FilterT(CC&& criteria, std::string name, FF lambda, std::int64_t id)
451 : AbstractElementT<C>::AbstractElementT(std::forward<CC>(criteria), id),
452 _name(std::move(name)),
453 _lambda(std::move(lambda))
454 {
455 }
456
457 [[nodiscard]] std::string toString() const final { return "f" + AbstractElementT<C>::toString(); }
458
459 [[nodiscard]] bool match(const std::shared_ptr<Filterable>& value) const final
460 {
461 return _lambda(std::static_pointer_cast<V>(value)->get());
462 }
463
464 [[nodiscard]] const std::string& getName() const final { return _name; }
465
466 using BaseClassType = Filter;
467
468 private:
469 std::string _name;
470 std::function<bool(const typename std::remove_reference<decltype(std::declval<V>().get())>::type&)> _lambda;
471 };
472
473 template<typename C, typename V>
474 class FilterFactoryT final : public FilterFactory, public AbstractFactoryT<C, FilterT<C, V>>
475 {
476 public:
477 FilterFactoryT() = default;
478
479 [[nodiscard]] std::shared_ptr<Filter> get(std::int64_t id) const final
480 {
481 return AbstractFactoryT<C, FilterT<C, V>>::getImpl(id);
482 }
483
484 [[nodiscard]] static std::shared_ptr<FilterFactoryT<C, V>> createFactory()
485 {
486 auto f = std::make_shared<FilterFactoryT<C, V>>();
487 f->init();
488 return f;
489 }
490 };
491
492 template<typename ValueT> class FilterManagerT final : public FilterManager
493 {
494 using Value = std::remove_reference_t<decltype(std::declval<ValueT>().get())>;
495
496 struct Factory
497 {
498 virtual ~Factory() = default;
499
500 [[nodiscard]] virtual std::shared_ptr<Filter> get(std::int64_t) const = 0;
501
502 [[nodiscard]] virtual std::shared_ptr<Filter> decode(const Ice::CommunicatorPtr&, const Ice::ByteSeq&) = 0;
503 };
504
505 template<typename Criteria> struct FactoryT final : Factory
506 {
507 FactoryT(std::string name, std::function<std::function<bool(const Value&)>(const Criteria&)> lambda)
508 : name(std::move(name)),
509 lambda(std::move(lambda))
510 {
511 }
512
513 [[nodiscard]] std::shared_ptr<Filter> create(const Criteria& criteria)
514 {
515 return std::static_pointer_cast<FilterT<Criteria, ValueT>>(
516 filterFactory.create(criteria, name, lambda(criteria)));
517 }
518
519 [[nodiscard]] std::shared_ptr<Filter> get(std::int64_t id) const final { return filterFactory.get(id); }
520
521 [[nodiscard]] std::shared_ptr<Filter>
522 decode(const Ice::CommunicatorPtr& communicator, const Ice::ByteSeq& data) final
523 {
524 return create(DecoderT<Criteria>::decode(communicator, data));
525 }
526
527 const std::string name;
528 std::function<std::function<bool(const Value&)>(const Criteria&)> lambda;
529 FilterFactoryT<Criteria, ValueT> filterFactory;
530 };
531
532 public:
533 template<typename Criteria>
534 [[nodiscard]] std::shared_ptr<Filter> create(const std::string& name, const Criteria& criteria)
535 {
536 auto p = _factories.find(name);
537 if (p == _factories.end())
538 {
539 throw std::invalid_argument("unknown filter '" + name + "'");
540 }
541
542 auto factory = dynamic_cast<FactoryT<Criteria>*>(p->second.get());
543 if (!factory)
544 {
545 throw std::invalid_argument("filter '" + name + "' type doesn't match");
546 }
547
548 return factory->create(criteria);
549 }
550
551 [[nodiscard]] std::shared_ptr<Filter>
552 decode(const Ice::CommunicatorPtr& communicator, const std::string& name, const Ice::ByteSeq& data) final
553 {
554 auto p = _factories.find(name);
555 if (p == _factories.end())
556 {
557 return nullptr;
558 }
559
560 return p->second->decode(communicator, data);
561 }
562
563 [[nodiscard]] std::shared_ptr<Filter> get(const std::string& name, std::int64_t id) const final
564 {
565 auto p = _factories.find(name);
566 if (p == _factories.end())
567 {
568 return nullptr;
569 }
570
571 return p->second->get(id);
572 }
573
574 template<typename Criteria>
575 void set(std::string name, std::function<std::function<bool(const Value&)>(const Criteria&)> lambda)
576 {
577 if (lambda)
578 {
579 auto factory = std::make_unique<FactoryT<Criteria>>(name, std::move(lambda));
580 _factories.emplace(std::move(name), std::move(factory));
581 }
582 else
583 {
584 _factories.erase(name);
585 }
586 }
587
588 private:
589 // A map containing the filter factories, indexed by the filter name.
590 std::map<std::string, std::unique_ptr<Factory>> _factories;
591 };
592}
593
594#if defined(__clang__)
595# pragma clang diagnostic pop
596#elif defined(__GNUC__)
597# pragma GCC diagnostic pop
598#endif
A sample provides information about a data element update.
Definition DataStorm.h:31
SampleEvent
Describes the operation used by a data writer to update a data element.
Definition SampleEvent.h:35
@ Remove
The data writer removed the element.
Definition SampleEvent.h:46
@ PartialUpdate
The data writer partially updated the element.
Definition SampleEvent.h:43
Data-centric, broker-less publish/subscribe framework. C++ only.
Definition DataStorm.h:25
std::shared_ptr< Communicator > CommunicatorPtr
A shared pointer to a Communicator.
std::vector< std::byte > ByteSeq
A sequence of bytes.
static T clone(const T &value) noexcept
Clones the given value.
Definition Types.h:209
static T decode(const Ice::CommunicatorPtr &communicator, const Ice::ByteSeq &value)
Decodes a value.
Definition Types.h:234
static Ice::ByteSeq encode(const Ice::CommunicatorPtr &communicator, const T &value) noexcept
Encodes the given value.
Definition Types.h:223