Logo
ApraPipes 1.0
Loading...
Searching...
No Matches
PropertyMacros.h
1// ============================================================
2// File: declarative/PropertyMacros.h
3// Task D2: Property Binding System
4//
5// Provides DECLARE_PROPS macro for DRY property definitions.
6// Single definition generates: members, metadata, apply, get, set.
7// ============================================================
8
9#pragma once
10
11#include "Metadata.h"
12#include <map>
13#include <string>
14#include <variant>
15#include <vector>
16#include <type_traits>
17#include <stdexcept>
18#include <cstdint>
19
20namespace apra {
21
22// ============================================================
23// ScalarPropertyValue - variant type for property values
24// (Also defined in ModuleRegistry.h, but we need it here too)
25// ============================================================
26#ifndef APRA_SCALAR_PROPERTY_VALUE_DEFINED
27#define APRA_SCALAR_PROPERTY_VALUE_DEFINED
28using ScalarPropertyValue = std::variant<int64_t, double, bool, std::string>;
29#endif
30
31// ============================================================
32// Type-safe property application with automatic conversion
33// Applies a value from the map to a member variable
34// ============================================================
35template<typename T>
36inline void applyProp(
37 T& member,
38 const char* propName,
39 const std::map<std::string, ScalarPropertyValue>& values,
40 bool isRequired,
41 std::vector<std::string>& missingRequired
42) {
43 auto it = values.find(propName);
44 if (it == values.end()) {
45 if (isRequired) {
46 missingRequired.push_back(propName);
47 }
48 return; // Keep default value
49 }
50
51 std::visit([&member, propName](auto&& val) {
52 using V = std::decay_t<decltype(val)>;
53
54 if constexpr (std::is_same_v<T, std::string>) {
55 if constexpr (std::is_same_v<V, std::string>) {
56 member = val;
57 }
58 }
59 else if constexpr (std::is_same_v<T, bool>) {
60 if constexpr (std::is_same_v<V, bool>) {
61 member = val;
62 }
63 }
64 else if constexpr (std::is_integral_v<T>) {
65 if constexpr (std::is_same_v<V, int64_t>) {
66 member = static_cast<T>(val);
67 }
68 }
69 else if constexpr (std::is_floating_point_v<T>) {
70 if constexpr (std::is_same_v<V, double>) {
71 member = static_cast<T>(val);
72 }
73 else if constexpr (std::is_same_v<V, int64_t>) {
74 member = static_cast<T>(val);
75 }
76 }
77 }, it->second);
78}
79
80// ============================================================
81// Convert member to ScalarPropertyValue for getProperty()
82// ============================================================
83template<typename T>
84inline ScalarPropertyValue toPropertyValue(const T& member) {
85 if constexpr (std::is_same_v<T, std::string>) {
86 return member;
87 }
88 else if constexpr (std::is_same_v<T, bool>) {
89 return member;
90 }
91 else if constexpr (std::is_integral_v<T>) {
92 return static_cast<int64_t>(member);
93 }
94 else if constexpr (std::is_floating_point_v<T>) {
95 return static_cast<double>(member);
96 }
97 else {
98 // Unsupported type - return empty string as fallback
99 return std::string("");
100 }
101}
102
103// ============================================================
104// Apply value from variant to member for setProperty()
105// Returns true if successfully applied
106// ============================================================
107template<typename T>
108inline bool applyFromVariant(T& member, const ScalarPropertyValue& value) {
109 return std::visit([&member](auto&& val) -> bool {
110 using V = std::decay_t<decltype(val)>;
111
112 if constexpr (std::is_same_v<T, std::string> && std::is_same_v<V, std::string>) {
113 member = val;
114 return true;
115 }
116 else if constexpr (std::is_same_v<T, bool> && std::is_same_v<V, bool>) {
117 member = val;
118 return true;
119 }
120 else if constexpr (std::is_integral_v<T> && std::is_same_v<V, int64_t>) {
121 member = static_cast<T>(val);
122 return true;
123 }
124 else if constexpr (std::is_floating_point_v<T> && std::is_same_v<V, double>) {
125 member = static_cast<T>(val);
126 return true;
127 }
128 else if constexpr (std::is_floating_point_v<T> && std::is_same_v<V, int64_t>) {
129 member = static_cast<T>(val);
130 return true;
131 }
132 return false;
133 }, value);
134}
135
136// ============================================================
137// PropertyInfo - runtime property metadata
138// ============================================================
140 std::string name;
141 std::string type;
142 std::string description;
143 bool required = false;
144 bool dynamic = false; // true = can change at runtime
145};
146
147} // namespace apra
148
149// ============================================================
150// X-Macro Helpers
151// ============================================================
152
153// Mutability markers (matches PropDef::Mutability in Metadata.h)
154#define APRA_PROP_MUT_Static false
155#define APRA_PROP_MUT_Dynamic true
156
157// Requirement markers
158#define APRA_PROP_REQ_Required true
159#define APRA_PROP_REQ_Optional false
160
161// ============================================================
162// PROP_DECL_MEMBER - generates member variable with default
163// P(type, name, mutability, requirement, default, description)
164// ============================================================
165#define APRA_PROP_DECL_MEMBER(type, name, mut, req, def, desc) \
166 type name = def;
167
168// ============================================================
169// PROP_INFO - generates PropertyInfo entry
170// ============================================================
171#define APRA_PROP_INFO(type, name, mut, req, def, desc) \
172 { #name, #type, desc, APRA_PROP_REQ_##req, APRA_PROP_MUT_##mut },
173
174// ============================================================
175// PROP_APPLY - generates applyProp call
176// ============================================================
177#define APRA_PROP_APPLY(type, name, mut, req, def, desc) \
178 apra::applyProp(props.name, #name, values, APRA_PROP_REQ_##req, missingRequired);
179
180// ============================================================
181// PROP_GET - generates getProperty case
182// ============================================================
183#define APRA_PROP_GET(type, name, mut, req, def, desc) \
184 if (propName == #name) return apra::toPropertyValue(name);
185
186// ============================================================
187// PROP_SET - generates setProperty case with mutability check
188// ============================================================
189#define APRA_PROP_SET_IMPL_false(type, name, desc) \
190 if (propName == #name) { \
191 throw std::runtime_error("Cannot modify static property '" #name "' after initialization"); \
192 }
193
194#define APRA_PROP_SET_IMPL_true(type, name, desc) \
195 if (propName == #name) { \
196 if (apra::applyFromVariant(name, value)) return true; \
197 throw std::runtime_error("Type mismatch for property '" #name "'"); \
198 }
199
200// Three-level indirection to force expansion before token pasting
201#define APRA_PROP_SET_CALL(impl, type, name, desc) impl(type, name, desc)
202#define APRA_PROP_SET_CONCAT(x, y) x ## y
203#define APRA_PROP_SET_DISPATCH(isDynamic, type, name, desc) \
204 APRA_PROP_SET_CALL(APRA_PROP_SET_CONCAT(APRA_PROP_SET_IMPL_, isDynamic), type, name, desc)
205
206#define APRA_PROP_SET(type, name, mut, req, def, desc) \
207 APRA_PROP_SET_DISPATCH(APRA_PROP_MUT_##mut, type, name, desc)
208
209// ============================================================
210// PROP_DYN_NAME - generates dynamic property name if applicable
211// ============================================================
212#define APRA_PROP_DYN_NAME_false(name)
213#define APRA_PROP_DYN_NAME_true(name) names.push_back(#name);
214
215// Three-level indirection to force expansion before token pasting
216#define APRA_PROP_DYN_CALL(impl, name) impl(name)
217#define APRA_PROP_DYN_CONCAT(x, y) x ## y
218#define APRA_PROP_DYN_DISPATCH(isDynamic, name) \
219 APRA_PROP_DYN_CALL(APRA_PROP_DYN_CONCAT(APRA_PROP_DYN_NAME_, isDynamic), name)
220
221#define APRA_PROP_DYN_NAME(type, name, mut, req, def, desc) \
222 APRA_PROP_DYN_DISPATCH(APRA_PROP_MUT_##mut, name)
223
224// ============================================================
225// DECLARE_PROPS - Main macro that generates everything
226//
227// Usage:
228// #define MY_PROPS(P) \
229// P(std::string, path, Static, Required, "", "File path") \
230// P(bool, loop, Static, Optional, false, "Loop playback") \
231// P(float, scale, Dynamic, Optional, 1.0f, "Scale factor")
232//
233// class MyModuleProps : public ModuleProps {
234// public:
235// DECLARE_PROPS(MY_PROPS)
236// MyModuleProps() : ModuleProps() {}
237// };
238// ============================================================
239#define DECLARE_PROPS(PROPS_MACRO) \
240 /* 1. Declare member variables with defaults */ \
241 PROPS_MACRO(APRA_PROP_DECL_MEMBER) \
242 \
243 /* 2. Get property metadata for introspection */ \
244 static std::vector<apra::PropertyInfo> getPropertyInfos() { \
245 return { \
246 PROPS_MACRO(APRA_PROP_INFO) \
247 }; \
248 } \
249 \
250 /* 3. Apply properties from TOML/map at construction */ \
251 template<typename PropsT> \
252 static void applyProperties( \
253 PropsT& props, \
254 const std::map<std::string, apra::ScalarPropertyValue>& values, \
255 std::vector<std::string>& missingRequired \
256 ) { \
257 PROPS_MACRO(APRA_PROP_APPLY) \
258 } \
259 \
260 /* 4. Get property by name (runtime introspection) */ \
261 apra::ScalarPropertyValue getProperty(const std::string& propName) const { \
262 PROPS_MACRO(APRA_PROP_GET) \
263 throw std::runtime_error("Unknown property: " + propName); \
264 } \
265 \
266 /* 5. Set property by name (only Dynamic properties allowed) */ \
267 bool setProperty(const std::string& propName, const apra::ScalarPropertyValue& value) { \
268 PROPS_MACRO(APRA_PROP_SET) \
269 throw std::runtime_error("Unknown property: " + propName); \
270 } \
271 \
272 /* 6. Get list of dynamic (runtime-modifiable) property names */ \
273 static std::vector<std::string> dynamicPropertyNames() { \
274 std::vector<std::string> names; \
275 PROPS_MACRO(APRA_PROP_DYN_NAME) \
276 return names; \
277 } \
278 \
279 /* 7. Check if a property is dynamic */ \
280 static bool isPropertyDynamic(const std::string& propName) { \
281 auto names = dynamicPropertyNames(); \
282 return std::find(names.begin(), names.end(), propName) != names.end(); \
283 }
284
285// End of PropertyMacros.h
Definition FrameTypeRegistrations.h:10
void applyProp(T &member, const char *propName, const std::map< std::string, ScalarPropertyValue > &values, bool isRequired, std::vector< std::string > &missingRequired)
Definition PropertyMacros.h:36
ScalarPropertyValue toPropertyValue(const T &member)
Definition PropertyMacros.h:84
bool applyFromVariant(T &member, const ScalarPropertyValue &value)
Definition PropertyMacros.h:108
std::variant< int64_t, double, bool, std::string > ScalarPropertyValue
Definition ModuleRegistry.h:30
Definition PropertyMacros.h:139
std::string name
Definition PropertyMacros.h:140
std::string description
Definition PropertyMacros.h:142
std::string type
Definition PropertyMacros.h:141
bool dynamic
Definition PropertyMacros.h:144
bool required
Definition PropertyMacros.h:143