open62541pp 0.16.0
C++ wrapper of open62541
Loading...
Searching...
No Matches
contextmap.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <functional> // invoke
4#include <map>
5#include <memory>
6#include <mutex>
7#include <type_traits>
8
9namespace opcua::detail {
10
11/**
12 * Check if an object as a boolean `stale` flag.
13 *
14 * Context objects e.g. for MonitoredItems may have a lifetime and a callback might be called when
15 * the item is deleted. So the context objects should be able to delete itself.
16 * The context object passed to the callback as a parameter but the callback has no access to the
17 * container owning this context object. Each context object could carry a reference to its owning
18 * container and delete itself, but this can be problematic... Instead a simple flag `stale` can be
19 * set if the item is deleted and should be removed from its container, the `ContextMap`.
20 */
21template <typename T, typename = void>
22struct IsStaleable : std::false_type {};
23
24template <typename T>
25struct IsStaleable<T, std::void_t<decltype(std::declval<T>().stale)>>
26 : std::is_same<decltype(std::declval<T>().stale), bool> {};
27
28/**
29 * Thread-safe map for context objects.
30 * Context objects are reference as `void*` pointers in open62541 functions/callbacks. To prevent
31 * pointer-invalidation, the objects are stored as unique pointers.
32 * Stale objects will be removed when new objects are stored in the map.
33 */
34template <typename Key, typename Item>
36public:
37 /// Access or insert specified element
38 Item* operator[](Key key) {
39 eraseStale();
40 auto lock = acquireLock();
41 auto& item = map_[key];
42 if (item == nullptr) {
43 item = std::make_unique<Item>(); // allocate item if empty
44 }
45 return item.get();
46 }
47
48 /// Inserts an element or assigns to the current element if the key already exists
49 Item* insert(Key key, std::unique_ptr<Item>&& item) {
50 eraseStale();
51 auto lock = acquireLock();
52 return map_.insert_or_assign(key, std::move(item)).first->second.get();
53 }
54
55 size_t erase(Key key) {
56 auto lock = acquireLock();
57 return map_.erase(key);
58 }
59
60 size_t eraseStale() {
61 const size_t count = map_.size();
62 if constexpr (IsStaleable<Item>::value) {
63 auto lock = acquireLock();
64 for (auto it = map_.begin(); it != map_.end();) {
65 if (it->second->stale) {
66 it = map_.erase(it);
67 } else {
68 ++it;
69 }
70 }
71 }
72 return count - map_.size();
73 }
74
75 bool contains(Key key) const {
76 auto lock = acquireLock();
77 return map_.count(key) > 0;
78 }
79
80 const Item* find(Key key) const {
81 auto lock = acquireLock();
82 auto it = map_.find(key);
83 if (it != map_.end()) {
84 return it->second.get();
85 }
86 return nullptr;
87 }
88
89 template <typename F>
90 void iterate(F&& func) const { // NOLINT(cppcoreguidelines-missing-std-forward)
91 auto lock = acquireLock();
92 for (const auto& pair : map_) {
93 std::invoke(func, pair);
94 }
95 }
96
97private:
98 [[nodiscard]] auto acquireLock() const {
99 return std::unique_lock(mutex_);
100 }
101
102 std::map<Key, std::unique_ptr<Item>> map_;
103 mutable std::mutex mutex_;
104};
105
106} // namespace opcua::detail
Thread-safe map for context objects.
void iterate(F &&func) const
const Item * find(Key key) const
bool contains(Key key) const
Item * operator[](Key key)
Access or insert specified element.
Item * insert(Key key, std::unique_ptr< Item > &&item)
Inserts an element or assigns to the current element if the key already exists.
Check if an object as a boolean stale flag.