Line data Source code
1 : /**
2 : * @file prop.h
3 : * @brief Type-agnotic property system
4 : *
5 : * This file provides a type-agnostic property system.
6 : * It is used to provide a generic interface for convenient property access for objects,
7 : * entities and entire systems.
8 : */
9 :
10 : #ifndef PROP_H_
11 : #define PROP_H_
12 :
13 : #include <any>
14 : #include <variant>
15 :
16 : #include "cube.h"
17 :
18 : namespace prop
19 : {
20 :
21 : /**
22 : * @brief Storage for a property value
23 : *
24 : * This type is used to store a property value.
25 : * Supported types are:
26 : * int, float, bvec, ivec, vec, std::string
27 : */
28 : typedef std::variant<int, float, bvec, ivec, vec, std::string> PropertyValue;
29 :
30 : /**
31 : * @brief Callback type for property changes
32 : *
33 : * @param argument - The argument passed to the callback.
34 : */
35 : typedef std::function<void(std::any argument)> OnChangeCallback;
36 :
37 : /**
38 : * @brief Type of a property
39 : *
40 : * This enum is used to determine the type of a property.
41 : * Mandates what sort of type is stored inside the PropertyValue.
42 : */
43 : enum class PropertyType
44 : {
45 : NoType = -1,
46 :
47 : Int,
48 : Float,
49 : Color,
50 : IntVec,
51 : FloatVec,
52 : String,
53 :
54 : Count
55 : };
56 :
57 : /**
58 : * @brief Meta information about a property
59 : *
60 : * This class stores definition information for a property:
61 : * - name (string)
62 : * - type (PropertyType)
63 : * - min, default and max values (PropertyValue)
64 : *
65 : * All properties should be statically bound to their PropertyMeta counterparts.
66 : */
67 : class PropertyMeta
68 : {
69 : private:
70 : std::string name;
71 : PropertyType type;
72 : PropertyValue min, def, max;
73 : OnChangeCallback on_change;
74 :
75 : public:
76 : PropertyType get_type() const;
77 : PropertyValue get_min() const;
78 : PropertyValue get_def() const;
79 : PropertyValue get_max() const;
80 : std::string get_name() const;
81 :
82 : void changed(std::any argument) const;
83 :
84 16 : template<class T> PropertyMeta(std::string _name, PropertyType _type, T _min, T _def, T _max,
85 : OnChangeCallback _on_change = nullptr) :
86 16 : name(_name), type(_type), min(_min), def(_def), max(_max), on_change(_on_change) {}
87 2 : template<class T> PropertyMeta(std::string _name, PropertyType _type, T _def,
88 : OnChangeCallback _on_change = nullptr) :
89 2 : name(_name), type(_type), def(_def), on_change(_on_change) {}
90 :
91 : PropertyMeta(const PropertyMeta&) = delete;
92 : };
93 :
94 : /**
95 : * @brief A property
96 : *
97 : * @tparam PropertyMetaT - The type of the meta class, defaults to PropertyMeta
98 : *
99 : * This class is an instance of a property. It stores a value and a reference to its meta class.
100 : */
101 : template<typename PropertyMetaT = PropertyMeta>
102 : class Property
103 : {
104 : private:
105 : PropertyValue value;
106 : const PropertyMetaT& meta;
107 :
108 : bool set_check_type(PropertyType type) const;
109 : void set_clamped(PropertyValue value);
110 :
111 : public:
112 : const PropertyValue& get_value() const;
113 : int get_int() const;
114 : float get_float() const;
115 : const bvec& get_color() const;
116 : const ivec& get_ivec() const;
117 : const vec& get_fvec() const;
118 : const std::string& get_string() const;
119 : size_t get_size() const;
120 : std::string get_name() const;
121 : PropertyType get_type() const;
122 :
123 : std::string to_string() const;
124 :
125 : void copy(const Property& prop);
126 :
127 : void set(PropertyValue value, std::any on_change_argument = std::any());
128 : void set_no_cb(PropertyValue value);
129 :
130 : void reset();
131 :
132 : void cmd_result() const;
133 : void cmd_result_min() const;
134 : void cmd_result_def() const;
135 : void cmd_result_max() const;
136 :
137 : virtual void pack(std::vector<uint8_t>& buf) const;
138 : virtual size_t unpack(const uint8_t* buf, size_t len);
139 :
140 : Property(const PropertyMetaT& _meta);
141 19 : virtual ~Property() = default;
142 : };
143 :
144 :
145 :
146 : ////////////////////
147 : // Implementation //
148 : ////////////////////
149 :
150 :
151 :
152 : /**
153 : * @brief Get the integer value of the property
154 : *
155 : * @tparam PropertyMetaT - The type of the meta class.
156 : * @return int
157 : *
158 : * Caution: This function does not check the type of the property. A former check is assumed.
159 : */
160 : template<typename PropertyMetaT>
161 7 : int Property<PropertyMetaT>::get_int() const
162 : {
163 7 : return std::get<int>(value);
164 : }
165 :
166 : /**
167 : * @brief Get the float value of the property
168 : *
169 : * @tparam PropertyMetaT - The type of the meta class.
170 : * @return float
171 : *
172 : * Caution: This function does not check the type of the property. A former check is assumed.
173 : */
174 : template<typename PropertyMetaT>
175 7 : float Property<PropertyMetaT>::get_float() const
176 : {
177 7 : return std::get<float>(value);
178 : }
179 :
180 : /**
181 : * @brief Get the color value of the property
182 : *
183 : * @tparam PropertyMetaT - The type of the meta class.
184 : * @return bvec
185 : *
186 : * Caution: This function does not check the type of the property. A former check is assumed.
187 : */
188 : template<typename PropertyMetaT>
189 7 : const bvec& Property<PropertyMetaT>::get_color() const
190 : {
191 7 : return std::get<bvec>(value);
192 : }
193 :
194 : /**
195 : * @brief Get the integer vector value of the property
196 : *
197 : * @tparam PropertyMetaT - The type of the meta class.
198 : * @return ivec
199 : *
200 : * Caution: This function does not check the type of the property. A former check is assumed.
201 : */
202 : template<typename PropertyMetaT>
203 8 : const ivec& Property<PropertyMetaT>::get_ivec() const
204 : {
205 8 : return std::get<ivec>(value);
206 : }
207 :
208 : /**
209 : * @brief Get the float vector value of the property
210 : *
211 : * @tparam PropertyMetaT - The type of the meta class.
212 : * @return vec
213 : *
214 : * Caution: This function does not check the type of the property. A former check is assumed.
215 : */
216 : template<typename PropertyMetaT>
217 8 : const vec& Property<PropertyMetaT>::get_fvec() const
218 : {
219 8 : return std::get<vec>(value);
220 : }
221 :
222 : /**
223 : * @brief Get the string value of the property
224 : *
225 : * @tparam PropertyMetaT - The type of the meta class.
226 : * @return std::string
227 : *
228 : * Caution: This function does not check the type of the property. A former check is assumed.
229 : */
230 : template<typename PropertyMetaT>
231 6 : const std::string& Property<PropertyMetaT>::get_string() const
232 : {
233 6 : return std::get<std::string>(value);
234 : }
235 :
236 : /**
237 : * @brief Get the size of the property
238 : *
239 : * @tparam PropertyMetaT - The type of the meta class.
240 : * @return size_t
241 : *
242 : * Returns the size of the stored value in bytes.
243 : */
244 : template<typename PropertyMetaT>
245 24 : size_t Property<PropertyMetaT>::get_size() const
246 : {
247 24 : switch(get_type())
248 : {
249 5 : case PropertyType::Int:
250 5 : return sizeof(int);
251 5 : case PropertyType::Float:
252 5 : return sizeof(float);
253 5 : case PropertyType::Color:
254 5 : return sizeof(bvec);
255 4 : case PropertyType::IntVec:
256 4 : return sizeof(ivec);
257 4 : case PropertyType::FloatVec:
258 4 : return sizeof(vec);
259 1 : case PropertyType::String:
260 1 : return get_string().size();
261 0 : default:
262 0 : return 0;
263 : }
264 : }
265 :
266 : template<typename PropertyMetaT>
267 6 : std::string Property<PropertyMetaT>::to_string() const
268 : {
269 6 : switch(get_type())
270 : {
271 1 : case PropertyType::Int:
272 1 : return std::to_string(get_int());
273 :
274 1 : case PropertyType::Float:
275 1 : return std::to_string(get_float());
276 :
277 1 : case PropertyType::Color:
278 1 : return std::to_string(get_color().r) + " " + std::to_string(get_color().g) + " " + std::to_string(get_color().b);
279 :
280 1 : case PropertyType::IntVec:
281 1 : return std::to_string(get_ivec().x) + " " + std::to_string(get_ivec().y) + " " + std::to_string(get_ivec().z);
282 :
283 1 : case PropertyType::FloatVec:
284 1 : return std::to_string(get_fvec().x) + " " + std::to_string(get_fvec().y) + " " + std::to_string(get_fvec().z);
285 :
286 1 : case PropertyType::String:
287 1 : return get_string();
288 :
289 0 : default:
290 0 : return "";
291 : }
292 : }
293 :
294 : template<typename PropertyMetaT>
295 31 : bool Property<PropertyMetaT>::set_check_type(PropertyType type) const
296 : {
297 : // typename used in debug printout below
298 : //static constexpr std::string_view typenames[] =
299 : //{
300 : // "int", "float", "color", "int vector", "float vector", "string"
301 : //};
302 :
303 31 : if(get_type() != type)
304 : {
305 : // conoutf("Error: %s, incorrect property assignment type: got %s, expected %s",
306 : // meta.get_name().c_str(), std::string(typenames[static_cast<size_t>(type)]).c_str(),
307 : // std::string(typenames[static_cast<size_t>(get_type())]).c_str());
308 :
309 0 : return false;
310 : }
311 :
312 31 : return true;
313 : }
314 :
315 : /**
316 : * @brief Get the name of the property
317 : *
318 : * @tparam PropertyMetaT - The type of the meta class.
319 : * @return std::string
320 : *
321 : * Returns the name of the property, as per the definition in the meta class.
322 : */
323 : template<typename PropertyMetaT>
324 227 : std::string Property<PropertyMetaT>::get_name() const
325 : {
326 227 : return meta.get_name();
327 : }
328 :
329 : /**
330 : * @brief Get the type of the property
331 : *
332 : * @tparam PropertyMetaT - The type of the meta class.
333 : * @return std::string
334 : *
335 : * Returns the type of the property, as per the definition in the meta class.
336 : */
337 : template<typename PropertyMetaT>
338 116 : PropertyType Property<PropertyMetaT>::get_type() const
339 : {
340 116 : return meta.get_type();
341 : }
342 :
343 : /**
344 : * @brief Copy the value of another property, will not invoke callback
345 : *
346 : * @tparam PropertyMetaT - The type of the meta class.
347 : * @param prop - The property to copy the value from.
348 : * @param on_change_arg - Optional argument for on_change callback.
349 : *
350 : * Note: type check is performed automatically, value of an incompatible type is ignored and reported.
351 : * Value is clamped to the range defined in the meta class, if applicable.
352 : */
353 : template<typename PropertyMetaT>
354 : void Property<PropertyMetaT>::copy(const Property& prop)
355 : {
356 : set_no_cb(prop.value);
357 : }
358 :
359 : template<typename PropertyMetaT>
360 31 : void Property<PropertyMetaT>::set_clamped(PropertyValue value)
361 : {
362 31 : switch(get_type())
363 : {
364 7 : case PropertyType::Int:
365 : {
366 7 : int v = std::get<int>(value);
367 7 : this->value = std::clamp(v, std::get<int>(meta.get_min()), std::get<int>(meta.get_max()));
368 7 : break;
369 : }
370 :
371 6 : case PropertyType::Float:
372 : {
373 6 : float v = std::get<float>(value);
374 6 : this->value = std::clamp(v, std::get<float>(meta.get_min()), std::get<float>(meta.get_max()));
375 6 : break;
376 : }
377 :
378 5 : case PropertyType::IntVec:
379 : {
380 5 : ivec v = std::get<ivec>(value);
381 10 : this->value = ivec(v).min(std::get<ivec>(meta.get_max()))
382 10 : .max(std::get<ivec>(meta.get_min()));
383 5 : break;
384 : }
385 :
386 5 : case PropertyType::FloatVec:
387 : {
388 5 : vec v = std::get<vec>(value);
389 10 : this->value = vec(v).min(std::get<vec>(meta.get_max()))
390 10 : .max(std::get<vec>(meta.get_min()));
391 5 : break;
392 : }
393 :
394 8 : default:
395 8 : this->value = value;
396 8 : break;
397 : }
398 31 : }
399 :
400 : /**
401 : * @brief Set the value of the property
402 : *
403 : * @tparam PropertyMetaT - The type of the meta class.
404 : * @param value - The new value of the property.
405 : * @param on_change_arg - Optional argument for on_change callback.
406 : *
407 : * Note: type check is performed automatically, value of an incompatible type is ignored and reported.
408 : * Value is clamped to the range defined in the meta class, if applicable.
409 : */
410 : template<typename PropertyMetaT>
411 31 : void Property<PropertyMetaT>::set(PropertyValue value, std::any on_change_arg)
412 : {
413 31 : if(set_check_type(static_cast<PropertyType>(value.index())))
414 : {
415 31 : set_clamped(value);
416 31 : meta.changed(on_change_arg);
417 : }
418 31 : }
419 :
420 : /**
421 : * @brief Set the value of the property without invoking callback
422 : *
423 : * @tparam PropertyMetaT - The type of the meta class.
424 : * @param value - The new value of the property.
425 : *
426 : * Note: type check is performed automatically, value of an incompatible type is ignored and reported.
427 : * Value is clamped to the range defined in the meta class, if applicable.
428 : */
429 : template<typename PropertyMetaT>
430 0 : void Property<PropertyMetaT>::set_no_cb(PropertyValue value)
431 : {
432 0 : if(set_check_type(static_cast<PropertyType>(value.index())))
433 : {
434 0 : set_clamped(value);
435 : }
436 0 : }
437 :
438 : /**
439 : * @brief Reset the value of the property to the default value
440 : *
441 : * @tparam PropertyMetaT - The type of the meta class.
442 : */
443 : template<typename PropertyMetaT>
444 30 : void Property<PropertyMetaT>::reset()
445 : {
446 30 : this->value = meta.get_def();
447 30 : }
448 :
449 6 : static void _prop_cmd_result_value(PropertyValue value, PropertyType type)
450 : {
451 6 : switch(type)
452 : {
453 1 : case PropertyType::Int:
454 1 : intret(std::get<int>(value));
455 1 : break;
456 :
457 1 : case PropertyType::Float:
458 1 : floatret(std::get<float>(value));
459 1 : break;
460 :
461 1 : case PropertyType::Color:
462 1 : intret(std::get<bvec>(value).tohexcolor());
463 1 : break;
464 :
465 1 : case PropertyType::IntVec:
466 : {
467 1 : const ivec& v = std::get<ivec>(value);
468 1 : result(tempformatstring("%d %d %d", v.x, v.y, v.z));
469 1 : break;
470 : }
471 :
472 1 : case PropertyType::FloatVec:
473 : {
474 1 : const vec& v = std::get<vec>(value);
475 1 : result(tempformatstring("%f %f %f", v.x, v.y, v.z));
476 1 : break;
477 : }
478 :
479 1 : case PropertyType::String:
480 1 : result(std::get<std::string>(value).c_str());
481 1 : break;
482 :
483 0 : default: break;
484 : }
485 6 : }
486 :
487 : /**
488 : * @brief Results the value of the property
489 : *
490 : * @tparam PropertyMetaT - The type of the meta class.
491 : */
492 : template<typename PropertyMetaT>
493 6 : void Property<PropertyMetaT>::cmd_result() const
494 : {
495 6 : _prop_cmd_result_value(value, get_type());
496 6 : }
497 :
498 : /**
499 : * @brief Results the minimum value of the property
500 : *
501 : * @tparam PropertyMetaT - The type of the meta class.
502 : */
503 : template<typename PropertyMetaT>
504 : void Property<PropertyMetaT>::cmd_result_min() const
505 : {
506 : _prop_cmd_result_value(meta.get_min(), get_type());
507 : }
508 :
509 : /**
510 : * @brief Results the default value of the property
511 : *
512 : * @tparam PropertyMetaT - The type of the meta class.
513 : */
514 : template<typename PropertyMetaT>
515 : void Property<PropertyMetaT>::cmd_result_def() const
516 : {
517 : _prop_cmd_result_value(meta.get_def(), get_type());
518 : }
519 :
520 : /**
521 : * @brief Results the maximum value of the property
522 : *
523 : * @tparam PropertyMetaT - The type of the meta class.
524 : */
525 : template<typename PropertyMetaT>
526 : void Property<PropertyMetaT>::cmd_result_max() const
527 : {
528 : _prop_cmd_result_value(meta.get_max(), get_type());
529 : }
530 :
531 : /**
532 : * @brief Packs the property into a buffer
533 : *
534 : * @tparam PropertyMetaT - The type of the meta class.
535 : * @param buf - The buffer to pack the property into.
536 : */
537 : template<typename PropertyMetaT>
538 6 : void Property<PropertyMetaT>::pack(std::vector<uint8_t>& buf) const
539 : {
540 6 : size_t data_size = get_size();
541 :
542 6 : vectorput(buf, data_size);
543 :
544 6 : switch(get_type())
545 : {
546 1 : case PropertyType::Int:
547 : {
548 1 : vectorput(buf, get_int());
549 1 : break;
550 : }
551 :
552 1 : case PropertyType::Float:
553 : {
554 1 : vectorput(buf, get_float());
555 1 : break;
556 : }
557 :
558 1 : case PropertyType::Color:
559 : {
560 1 : vectorput(buf, get_color());
561 1 : break;
562 : }
563 :
564 1 : case PropertyType::IntVec:
565 : {
566 1 : vectorput(buf, get_ivec());
567 1 : break;
568 : }
569 :
570 1 : case PropertyType::FloatVec:
571 : {
572 1 : vectorput(buf, get_fvec());
573 1 : break;
574 : }
575 :
576 1 : case PropertyType::String:
577 : {
578 1 : std::string data = get_string();
579 : // Null-terminator not needed, since we know the size
580 1 : vectorput(buf, reinterpret_cast<const uint8_t*>(data.data()), data.size());
581 1 : break;
582 1 : }
583 :
584 0 : default: break;
585 : }
586 6 : }
587 :
588 : /**
589 : * @brief Unpacks the property from a buffer
590 : *
591 : * @tparam PropertyMetaT - The type of the meta class.
592 : * @param buf - The buffer to unpack the property from.
593 : * @param buf_size - The size of the buffer.
594 : *
595 : * @return The number of bytes read from the buffer.
596 : */
597 : template<typename PropertyMetaT>
598 6 : size_t Property<PropertyMetaT>::unpack(const uint8_t* buf, size_t buf_size)
599 : {
600 6 : size_t buf_read = 0;
601 6 : const size_t* data_size_packed = nullptr;
602 :
603 6 : if(buf_size <= sizeof(*data_size_packed))
604 : {
605 : // conoutf("Error unpacking prop '%s': not enough data to get the size!", meta.get_name().c_str());
606 0 : return 0;
607 : }
608 :
609 6 : data_size_packed = reinterpret_cast<const size_t*>(buf);
610 :
611 6 : buf_read += sizeof(*data_size_packed);
612 :
613 6 : static auto size_check = [&](size_t needed_size)
614 : {
615 6 : if(buf_size - buf_read < needed_size)
616 : {
617 : // conoutf("Error unpacking prop '%s': not enough data!", meta.get_name().c_str());
618 0 : return false;
619 : }
620 :
621 6 : return true;
622 : };
623 :
624 6 : switch(get_type())
625 : {
626 3 : case PropertyType::Int:
627 : case PropertyType::Color:
628 : case PropertyType::Float:
629 3 : if(*data_size_packed != get_size())
630 : {
631 : // conoutf("Error unpacking prop '%s': unexpected data size! Wanted: %lu, got: %lu",
632 : // meta.get_name().c_str(), get_size(), *data_size_packed);
633 :
634 0 : return 0;
635 : }
636 3 : break;
637 :
638 3 : default: break;
639 : }
640 :
641 6 : switch(get_type())
642 : {
643 1 : case PropertyType::Int:
644 : {
645 1 : if(!size_check(get_size()))
646 : {
647 0 : return 0;
648 : }
649 :
650 : int ival;
651 1 : memcpy(&ival, buf + buf_read, get_size());
652 1 : buf_read += get_size();
653 1 : value = ival;
654 1 : break;
655 : }
656 :
657 1 : case PropertyType::Float:
658 : {
659 1 : if(!size_check(get_size()))
660 : {
661 0 : return 0;
662 : }
663 :
664 : float fval;
665 1 : memcpy(&fval, buf + buf_read, get_size());
666 1 : buf_read += get_size();
667 1 : value = fval;
668 1 : break;
669 : }
670 :
671 1 : case PropertyType::Color:
672 : {
673 1 : if(!size_check(get_size()))
674 : {
675 0 : return 0;
676 : }
677 :
678 1 : bvec cval;
679 1 : memcpy(&cval, buf + buf_read, get_size());
680 1 : buf_read += get_size();
681 1 : value = cval;
682 1 : break;
683 : }
684 :
685 1 : case PropertyType::IntVec:
686 : {
687 1 : if(!size_check(get_size()))
688 : {
689 0 : return 0;
690 : }
691 :
692 1 : ivec ivecval;
693 1 : memcpy(&ivecval, buf + buf_read, get_size());
694 1 : buf_read += get_size();
695 1 : value = ivecval;
696 1 : break;
697 : }
698 :
699 1 : case PropertyType::FloatVec:
700 : {
701 1 : if(!size_check(get_size()))
702 : {
703 0 : return 0;
704 : }
705 :
706 1 : vec vecval;
707 1 : memcpy(&vecval, buf + buf_read, get_size());
708 1 : buf_read += get_size();
709 1 : value = vecval;
710 1 : break;
711 : }
712 :
713 1 : case PropertyType::String:
714 : {
715 1 : if(!size_check(*data_size_packed))
716 : {
717 0 : return 0;
718 : }
719 :
720 1 : std::string sval(reinterpret_cast<const char*>(buf + buf_read), *data_size_packed);
721 1 : buf_read += *data_size_packed;
722 1 : value = sval;
723 1 : break;
724 1 : }
725 :
726 0 : default: break;
727 : }
728 :
729 6 : return buf_read;
730 : }
731 :
732 : /**
733 : * @brief Creates a new property with the given meta class.
734 : *
735 : * @tparam PropertyMetaT - The type of the meta class.
736 : * @param _meta - The meta class.
737 : */
738 : template<typename PropertyMetaT>
739 30 : Property<PropertyMetaT>::Property(const PropertyMetaT& _meta) : meta(_meta)
740 : {
741 30 : reset();
742 30 : }
743 :
744 : /**
745 : * @brief Gets the type of the property definition.
746 : *
747 : * @return PropertyType
748 : */
749 116 : PropertyType PropertyMeta::get_type() const
750 : {
751 116 : return type;
752 : }
753 :
754 : /**
755 : * @brief Gets the minimum value of the property definition.
756 : *
757 : * @return PropertyValue
758 : */
759 23 : PropertyValue PropertyMeta::get_min() const
760 : {
761 23 : return min;
762 : }
763 :
764 : /**
765 : * @brief Gets the default value of the property definition.
766 : *
767 : * @return PropertyValue
768 : */
769 30 : PropertyValue PropertyMeta::get_def() const
770 : {
771 30 : return def;
772 : }
773 :
774 : /**
775 : * @brief Gets the maximum value of the property definition.
776 : *
777 : * @return PropertyValue
778 : */
779 23 : PropertyValue PropertyMeta::get_max() const
780 : {
781 23 : return max;
782 : }
783 :
784 : /**
785 : * @brief Gets the name of the property definition.
786 : *
787 : * @return std::string
788 : */
789 299 : std::string PropertyMeta::get_name() const
790 : {
791 299 : return name;
792 : }
793 :
794 31 : void PropertyMeta::changed(std::any argument) const
795 : {
796 31 : if(on_change)
797 : {
798 1 : on_change(argument);
799 : }
800 31 : }
801 :
802 :
803 :
804 : //////////////////////
805 : // Helper functions //
806 : //////////////////////
807 :
808 :
809 :
810 : /**
811 : * @brief Finds a property with the given name in the given array.
812 : *
813 : * @tparam PropertyT - Property class type
814 : * @tparam N - Size of the array
815 : * @param name - The name of the property to find.
816 : * @param props - The array of properties to search.
817 : * @return PropertyT* - The property if found, nullptr otherwise.
818 : */
819 : template<typename PropertyT, std::size_t N>
820 62 : PropertyT* find_prop(std::string name, std::array<PropertyT, N>& props)
821 : {
822 217 : for(PropertyT& prop : props)
823 : {
824 216 : if(prop.get_name() == name)
825 : {
826 61 : return ∝
827 : }
828 : }
829 :
830 1 : return nullptr;
831 : }
832 :
833 : /**
834 : * @brief Finds a property with the given name in the given array.
835 : *
836 : * @tparam PropertyT - Property class type
837 : * @tparam N - Size of the array
838 : * @param name - The name of the property to find.
839 : * @param props - The array of properties to search.
840 : * @return PropertyT* - The property if found, nullptr otherwise.
841 : */
842 : template<typename PropertyT, std::size_t N>
843 1 : const PropertyT* find_prop(std::string name, const std::array<PropertyT, N>& props)
844 : {
845 12 : for(const PropertyT& prop : props)
846 : {
847 11 : if(prop.get_name() == name)
848 : {
849 0 : return ∝
850 : }
851 : }
852 :
853 1 : return nullptr;
854 : }
855 :
856 : /**
857 : * @brief Sets the value of a property with the given name in the given array.
858 : *
859 : * @tparam PropertyT - Property class type
860 : * @tparam N - Size of the array
861 : * @param name - The name of the property to set.
862 : * @param value - The value to set the property to. Value is clamped to the property's min/max values, if applicable.
863 : * @param props - The array of properties to search.
864 : * @param on_change_arg - Optional argument to pass to the property's on_change callback.
865 : * @return true - The property was found and set.
866 : */
867 : template<typename PropertyT, std::size_t N>
868 32 : bool set_prop(std::string name, PropertyValue value, std::array<PropertyT, N>& props,
869 : std::any on_change_arg = std::any())
870 : {
871 32 : PropertyT* prop = find_prop(name, props);
872 32 : if(!prop)
873 : {
874 : // conoutf("Property %s not found!", name.c_str());
875 1 : return false;
876 : }
877 :
878 31 : prop->set(value, on_change_arg);
879 31 : return true;
880 : }
881 :
882 : /**
883 : * @brief Finds a property definition with the given name in the given array.
884 : *
885 : * @tparam PropertyMetaT - PropertyMeta class type
886 : * @tparam N - Size of the array
887 : * @param name - The name of the property definition to find.
888 : * @param prop_metas - The array of property definitions to search.
889 : * @return const PropertyMetaT* - The property definition if found, nullptr otherwise.
890 : */
891 : template<typename PropertyMetaT, std::size_t N>
892 : const PropertyMetaT* find_prop_meta(std::string name, const std::array<PropertyMetaT, N>& prop_metas)
893 : {
894 : for(const PropertyMetaT& prop_meta : prop_metas)
895 : {
896 : if(prop_meta.get_name() == name)
897 : {
898 : return &prop_meta;
899 : }
900 : }
901 :
902 : return nullptr;
903 : }
904 :
905 : /**
906 : * @brief Packs the given properties into the given buffer.
907 : *
908 : * @tparam PropertyT - Property class type
909 : * @tparam N - Size of the array
910 : * @param props - The array of properties to pack.
911 : * @param buf - The buffer to pack the properties into.
912 : */
913 : template<typename PropertyT, std::size_t N>
914 1 : void pack_props(const std::array<PropertyT, N>& props, std::vector<uint8_t>& buf)
915 : {
916 7 : for(const PropertyT& prop : props)
917 : {
918 6 : prop.pack(buf);
919 : }
920 1 : }
921 :
922 : /**
923 : * @brief Unpacks the given buffer into the given properties.
924 : *
925 : * @tparam PropertyT - Property class type
926 : * @tparam N - Size of the array
927 : * @param buf - The buffer to unpack.
928 : * @param props - The array of properties to unpack into.
929 : * @return size_t - The number of properties unpacked.
930 : */
931 : template<typename PropertyT, std::size_t N>
932 1 : size_t unpack_props(const std::vector<uint8_t>& buf, std::array<PropertyT, N>& props)
933 : {
934 1 : size_t prop_idx = 0;
935 1 : size_t read_pos = 0;
936 :
937 7 : while(read_pos < buf.size())
938 : {
939 6 : if(prop_idx >= N)
940 : {
941 0 : break;
942 : }
943 :
944 6 : PropertyT &prop = props[prop_idx];
945 :
946 6 : int unpacked_size = prop.unpack(buf.data() + read_pos, buf.size() - read_pos);
947 6 : if(!unpacked_size)
948 : {
949 0 : break;
950 : }
951 :
952 6 : read_pos += unpacked_size;
953 6 : prop_idx++;
954 : }
955 :
956 1 : return prop_idx;
957 : }
958 :
959 : template<std::size_t N, typename PropertyT, std::size_t... Is, typename PropertyMetaT>
960 5 : std::array<PropertyT, N> _make_props_array_impl(std::index_sequence<Is...>,
961 : const PropertyMetaT (&prop_meta)[N])
962 : {
963 5 : return { PropertyT(prop_meta[Is])... };
964 : }
965 :
966 : /**
967 : * @brief Creates an array of properties from the given property definitions.
968 : *
969 : * @tparam N - Size of the array
970 : * @tparam PropertyT - Property class type
971 : * @tparam Is - Index sequence
972 : * @tparam PropertyMetaT - PropertyMeta class type
973 : * @param prop_meta - The array of property definitions to create properties from.
974 : * @return std::array<PropertyT, N> - The array of properties bound to meta information.
975 : */
976 : template<std::size_t N, typename PropertyT, std::size_t... Is, typename PropertyMetaT>
977 5 : std::array<PropertyT, N> make_props_array(const PropertyMetaT (&prop_meta)[N])
978 : {
979 5 : return _make_props_array_impl<N, PropertyT>(std::make_index_sequence<N>{}, prop_meta);
980 : }
981 :
982 : }; // namespace prop
983 :
984 : #endif // PROP_H_
|