libtcspc C++ API
Streaming TCSPC and time tag data processing
Loading...
Searching...
No Matches
context.hpp
1/*
2 * This file is part of libtcspc
3 * Copyright 2019-2026 Board of Regents of the University of Wisconsin System
4 * SPDX-License-Identifier: MIT
5 */
6
7#pragma once
8
9#include <any>
10#include <cassert>
11#include <cstddef>
12#include <functional>
13#include <memory>
14#include <stdexcept>
15#include <string>
16#include <unordered_map>
17#include <utility>
18
19namespace tcspc {
20
21class context;
22
39template <typename Access> class access_tracker {
40 std::shared_ptr<context> ctx; // Null iff 'empty' state.
41 std::string name;
42 std::function<Access(access_tracker &)> access_factory;
43
44 friend class context;
45
46 // Construct only via friend context.
47 explicit access_tracker(std::shared_ptr<context> context, std::string name)
48 : ctx(std::move(context)), name(std::move(name)) {}
49
50 public:
60 access_tracker() = default;
61
63 access_tracker(access_tracker const &) = delete;
64 auto operator=(access_tracker const &) = delete;
65
66 // Definitions for move and destruction are below, out of line.
67 access_tracker(access_tracker &&other) noexcept;
68 auto operator=(access_tracker &&rhs) noexcept -> access_tracker &;
69 ~access_tracker();
71
83 template <typename F> void register_access_factory(F factory) {
84 assert(ctx);
85 assert(not access_factory);
86 access_factory = std::move(factory);
87 }
88};
89
113class context : public std::enable_shared_from_this<context> {
114 // Map keys are names identifying the tracked object and values are pointer
115 // to access_tracker<Access> for some Access. Values are kept up to date
116 // when a tracker is moved or destroyed. If the tracked object has been
117 // destroyed, the entry remains and the value is an empty std::any()).
118 // Reuse of name is not allowed.
119 std::unordered_map<std::string, std::any> trackers;
120
121 template <typename Access> friend class access_tracker;
122
123 template <typename Access>
124 void update_tracker_address(std::string const &name,
125 access_tracker<Access> *ptr) {
126 // Enforce no type change in std::any; just update the value directly.
127 *std::any_cast<access_tracker<Access> *>(&trackers.at(name)) = ptr;
128 }
129
130 context() = default;
131
132 public:
133 // Move not permitted (only handled by shared_ptr).
135 context(context const &) = delete;
136 auto operator=(context const &) = delete;
137 context(context &&) = delete;
138 auto operator=(context &&) = delete;
139 ~context() = default;
141
145 static auto create() -> std::shared_ptr<context> {
146 return std::shared_ptr<context>(new context());
147 }
148
158 template <typename Access>
159 auto tracker(std::string name) -> access_tracker<Access> {
160 if (trackers.count(name) != 0) {
161 throw std::logic_error(
162 "cannot create tracker for existing name: " + name);
163 }
164 auto ret = access_tracker<Access>(shared_from_this(), std::move(name));
165 trackers.insert({ret.name, std::any(&ret)});
166 return ret;
167 }
168
179 template <typename Access> auto access(std::string const &name) -> Access {
180 auto tracker_ptr =
181 std::any_cast<access_tracker<Access> *>(trackers.at(name));
182 if (tracker_ptr == nullptr)
183 throw std::logic_error("cannot access destroyed object: " + name);
184 return tracker_ptr->access_factory(*tracker_ptr);
185 }
186};
187
189
190// Move/destroy for access_tracker, defined out-of-line because they require
191// the definition of context.
192
193template <typename Access>
194access_tracker<Access>::access_tracker(access_tracker &&other) noexcept
195 : ctx(std::move(other.ctx)), name(std::move(other.name)),
196 access_factory(std::move(other.access_factory)) {
197 if (ctx)
198 ctx->update_tracker_address(name, this);
199}
200
201template <typename Access>
203 -> access_tracker & {
204 ctx = std::move(rhs.ctx);
205 name = std::move(rhs.name);
206 access_factory = std::move(rhs.access_factory);
207 if (ctx)
208 ctx->update_tracker_address(name, this);
209 return *this;
210}
211
212template <typename Access> access_tracker<Access>::~access_tracker() {
213 if (ctx)
214 ctx->update_tracker_address<Access>(name, nullptr);
215}
216
218
219// NOLINTBEGIN
220
221// Locally silence GCC/Clang warnings for using offsetof() on
222// non-standard-layout types. Semicolons used only to assist clang-format.
223#if defined(__GNUC__) || defined(__clang__)
224#define LIBTCSPC_INTERNAL_DISABLE_OFFSETOF_WARNING \
225 _Pragma("GCC diagnostic push"); \
226 _Pragma("GCC diagnostic ignored \"-Winvalid-offsetof\"")
227#define LIBTCSPC_INTERNAL_POP_OFFSETOF_WARNING _Pragma("GCC diagnostic pop")
228#else
229#define LIBTCSPC_INTERNAL_DISABLE_OFFSETOF_WARNING
230#define LIBTCSPC_INTERNAL_POP_OFFSETOF_WARNING
231#endif
232
253#define LIBTCSPC_OBJECT_FROM_TRACKER(obj_type, tracker_field_name, tracker) \
254 std::invoke([&tracker]() { \
255 LIBTCSPC_INTERNAL_DISABLE_OFFSETOF_WARNING; \
256 return reinterpret_cast<std::add_pointer_t<obj_type>>( \
257 reinterpret_cast<std::byte *>(&(tracker)) - \
258 offsetof(obj_type, tracker_field_name)); \
259 LIBTCSPC_INTERNAL_POP_OFFSETOF_WARNING; \
260 })
261
262// Note: offsetof() is "conditionally-supported" on non-standard-layout types
263// as of C++17. I expect this not to be a problem in practice, as long as
264// virtual inheritance is not involved.
265
266// Pointer arithmetic on the underlying bytes of an object is technically
267// undefined behavior in C++ (but not C), because no array of bytes was created
268// there. But compilers treat this as valid (see https://wg21.link/p1839r6).
269// (Also see https://wg21.link/p3407r0 on another technical aspect.)
270
271// NOLINTEND
272
273} // namespace tcspc
Tracker that mediates access to objects via a tcspc::context.
Definition context.hpp:39
access_tracker()=default
Construct an empty tracker.
void register_access_factory(F factory)
Register an access factory with this tracker's context.
Definition context.hpp:83
Context for enabling access to objects after they have been incorporated into a processing graph.
Definition context.hpp:113
static auto create() -> std::shared_ptr< context >
Create an instance.
Definition context.hpp:145
auto access(std::string const &name) -> Access
Obtain an access for the named object.
Definition context.hpp:179
auto tracker(std::string name) -> access_tracker< Access >
Obtain a tracker for an object with the given name.
Definition context.hpp:159
libtcspc namespace.
Definition acquire.hpp:29