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 Accessor> class access_tracker {
40 std::shared_ptr<context> ctx; // Null iff 'empty' state.
41 std::string name;
42 std::function<Accessor(access_tracker &)> accessor_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_accessor_factory(F factory) {
84 assert(ctx);
85 assert(not accessor_factory);
86 accessor_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<Accessor> for some Accessor. Values are kept up to
116 // date when a tracker is moved or destroyed. If the tracked object has
117 // been 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 Accessor> friend class access_tracker;
122
123 template <typename Accessor>
124 void update_tracker_address(std::string const &name,
125 access_tracker<Accessor> *ptr) {
126 // Enforce no type change in std::any; just update the value directly.
127 *std::any_cast<access_tracker<Accessor> *>(&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 Accessor>
159 auto tracker(std::string name) -> access_tracker<Accessor> {
160 if (trackers.count(name) != 0) {
161 throw std::logic_error(
162 "cannot create tracker for existing name: " + name);
163 }
164 auto ret =
165 access_tracker<Accessor>(shared_from_this(), std::move(name));
166 trackers.insert({ret.name, std::any(&ret)});
167 return ret;
168 }
169
180 template <typename Accessor>
181 auto access(std::string const &name) -> Accessor {
182 auto tracker_ptr =
183 std::any_cast<access_tracker<Accessor> *>(trackers.at(name));
184 if (tracker_ptr == nullptr)
185 throw std::logic_error("cannot access destroyed object: " + name);
186 return tracker_ptr->accessor_factory(*tracker_ptr);
187 }
188};
189
191
192// Move/destroy for access_tracker, defined out-of-line because they require
193// the definition of context.
194
195template <typename Accessor>
196access_tracker<Accessor>::access_tracker(access_tracker &&other) noexcept
197 : ctx(std::move(other.ctx)), name(std::move(other.name)),
198 accessor_factory(std::move(other.accessor_factory)) {
199 if (ctx)
200 ctx->update_tracker_address(name, this);
201}
202
203template <typename Accessor>
205 -> access_tracker & {
206 ctx = std::move(rhs.ctx);
207 name = std::move(rhs.name);
208 accessor_factory = std::move(rhs.accessor_factory);
209 if (ctx)
210 ctx->update_tracker_address(name, this);
211 return *this;
212}
213
214template <typename Accessor> access_tracker<Accessor>::~access_tracker() {
215 if (ctx)
216 ctx->update_tracker_address<Accessor>(name, nullptr);
217}
218
220
221// NOLINTBEGIN
222
223// Locally silence GCC/Clang warnings for using offsetof() on
224// non-standard-layout types. Semicolons used only to assist clang-format.
225#if defined(__GNUC__) || defined(__clang__)
226#define LIBTCSPC_INTERNAL_DISABLE_OFFSETOF_WARNING \
227 _Pragma("GCC diagnostic push"); \
228 _Pragma("GCC diagnostic ignored \"-Winvalid-offsetof\"")
229#define LIBTCSPC_INTERNAL_POP_OFFSETOF_WARNING _Pragma("GCC diagnostic pop")
230#else
231#define LIBTCSPC_INTERNAL_DISABLE_OFFSETOF_WARNING
232#define LIBTCSPC_INTERNAL_POP_OFFSETOF_WARNING
233#endif
234
255#define LIBTCSPC_OBJECT_FROM_TRACKER(obj_type, tracker_field_name, tracker) \
256 std::invoke([&tracker]() { \
257 LIBTCSPC_INTERNAL_DISABLE_OFFSETOF_WARNING; \
258 return reinterpret_cast<std::add_pointer_t<obj_type>>( \
259 reinterpret_cast<std::byte *>(&(tracker)) - \
260 offsetof(obj_type, tracker_field_name)); \
261 LIBTCSPC_INTERNAL_POP_OFFSETOF_WARNING; \
262 })
263
264// Note: offsetof() is "conditionally-supported" on non-standard-layout types
265// as of C++17. I expect this not to be a problem in practice, as long as
266// virtual inheritance is not involved.
267
268// Pointer arithmetic on the underlying bytes of an object is technically
269// undefined behavior in C++ (but not C), because no array of bytes was created
270// there. But compilers treat this as valid (see https://wg21.link/p1839r6).
271// (Also see https://wg21.link/p3407r0 on another technical aspect.)
272
273// NOLINTEND
274
275} // 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_accessor_factory(F factory)
Register an accessor 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
auto access(std::string const &name) -> Accessor
Obtain an accessor for the named object.
Definition context.hpp:181
static auto create() -> std::shared_ptr< context >
Create an instance.
Definition context.hpp:145
auto tracker(std::string name) -> access_tracker< Accessor >
Obtain a tracker for an object with the given name.
Definition context.hpp:159
libtcspc namespace.
Definition acquire.hpp:30