1 // SPDX-License-Identifier: Apache-2.0
3 * Copyright (C) 2021 Jihoon Lee <jhoon.it.lee@samsung.com>
5 * @file base_properties.h
7 * @brief Convenient property type definition for automated serialization
8 * @see https://github.com/nnstreamer/nntrainer
9 * @author Jihoon Lee <jhoon.it.lee@samsung.com>
10 * @bug No known bugs except for NYI items
12 #ifndef __BASE_PROPERTIES_H__
13 #define __BASE_PROPERTIES_H__
22 #include <nntrainer_error.h>
23 #include <tensor_dim.h>
24 #include <util_func.h>
26 /** base and predefined structures */
30 using TensorDim = ml::train::TensorDim;
33 * @brief property info to specialize functions based on this
34 * @tparam T property type
36 template <typename T> struct prop_info {
37 using prop_type = std::decay_t<T>; /** property type of T */
38 using tag_type = typename prop_type::prop_tag; /** Property tag of T */
40 std::decay_t<decltype(std::declval<prop_type>().get())>; /** Underlying
45 * @brief property info when it is wrapped inside a vector
47 * @tparam T property type
49 template <typename T> struct prop_info<std::vector<T>> {
50 using prop_type = typename prop_info<T>::prop_type;
51 using tag_type = typename prop_info<T>::tag_type;
52 using data_type = typename prop_info<T>::data_type;
56 * @brief property info when it is wrapped inside an array
58 * @tparam T property type
60 template <typename T, size_t size> struct prop_info<std::array<T, size>> {
61 using prop_type = typename prop_info<T>::prop_type;
62 using tag_type = typename prop_info<T>::tag_type;
63 using data_type = typename prop_info<T>::data_type;
67 * @brief Get the Prop Key object
69 * @tparam T property to get type
70 * @param prop property
71 * @return constexpr const char* key
73 template <typename T> constexpr const char *getPropKey(T &&prop) {
74 return prop_info<std::decay_t<T>>::prop_type::key;
78 * @brief property is treated as integer
81 struct int_prop_tag {};
84 * @brief property is treated as unsigned integer
87 struct uint_prop_tag {};
90 * @brief property is treated as unsigned integer
93 struct size_t_prop_tag {};
96 * @brief property is treated as dimension, eg 1:2:3
99 struct dimension_prop_tag {};
102 * @brief property is treated as float
105 struct float_prop_tag {};
108 * @brief property is treated as double
111 struct double_prop_tag {};
114 * @brief property is treated as string
117 struct str_prop_tag {};
120 * @brief property is treated as boolean
123 struct bool_prop_tag {};
126 * @brief property is treated as enum class
129 struct enum_class_prop_tag {};
132 * @brief property treated as a raw pointer
135 struct ptr_prop_tag {};
138 * @brief base property class, inherit this to make a convenient property
142 template <typename T> class Property {
146 * @brief Construct a new Property object
149 Property() : value(nullptr){};
152 * @brief Construct a new Property object
154 * @param value default value
156 Property(const T &value_) { set(value_); }
159 * @brief Copy Construct a new Property object
161 * @param rhs right side to copy from
163 Property(const Property &rhs) {
164 if (this != &rhs && rhs.value) {
165 value = std::make_unique<T>(*rhs.value);
170 * @brief Copy assignment operator of a new property
172 * @param rhs right side to copy from
173 * @return Property& this
175 Property &operator=(const Property &rhs) {
176 if (this != &rhs && rhs.value) {
177 value = std::make_unique<T>(*rhs.value);
182 Property(Property &&rhs) noexcept = default;
183 Property &operator=(Property &&rhs) noexcept = default;
186 * @brief Destroy the Property object
189 virtual ~Property() = default;
192 * @brief cast operator for property
196 operator T &() { return get(); }
199 * @brief cast operator for property
203 operator const T &() const { return get(); }
206 * @brief get the underlying data
208 * @return const T& data
210 const T &get() const {
211 NNTR_THROW_IF(value == nullptr, std::invalid_argument)
212 << "Cannot get property, property is empty";
217 * @brief get the underlying data
222 NNTR_THROW_IF(value == nullptr, std::invalid_argument)
223 << "Cannot get property, property is empty";
228 * @brief check if property is empty
231 * @retval false not empty
233 bool empty() const { return value == nullptr; }
236 * @brief set the underlying data
238 * @param v value to set
239 * @throw std::invalid_argument if argument is not valid
241 virtual void set(const T &v) {
242 NNTR_THROW_IF(isValid(v) == false, std::invalid_argument)
243 << "argument is not valid";
244 value = std::make_unique<T>(v);
248 * @brief check if given value is valid
250 * @param v value to check
251 * @retval true if valid
252 * @retval false if not valid
254 virtual bool isValid(const T &v) const { return true; }
259 * @param rhs right side to compare
260 * @retval true if equal
261 * @retval false if not equal
263 bool operator==(const Property<T> &rhs) const { return *value == *rhs.value; }
266 std::unique_ptr<T> value; /**< underlying data */
270 * @brief enum property
272 * @tparam T underlying type info to query enum_info
274 template <typename EnumInfo>
275 class EnumProperty : public Property<typename EnumInfo::Enum> {
277 static EnumInfo enum_info_;
281 * @brief abstract class for positive integer
284 class PositiveIntegerProperty : public Property<unsigned int> {
287 * @brief Destroy the Positive Integer Property object
290 virtual ~PositiveIntegerProperty() = default;
293 * @brief isValid override, check if value > 0
295 * @param value value to check
296 * @retval true if value > 0
298 virtual bool isValid(const unsigned int &value) const override;
301 * @brief meta function to cast tag to it's base
302 * @code below is the test spec for the cast
304 * struct custom_tag: int_prop_tag {};
306 * using tag_type = tag_cast<custom_tag, float_prop_tag>::type
307 * static_assert(<std::is_same_v<tag_type, custom_tag> == true);
309 * using tag_type = tag_cast<custom_tag, int_prop_tag>::type
310 * static_assert(<std::is_same_v<tag_type, int_prop_tag> == true);
312 * using tag_type = tag_cast<custom_tag, float_prop_tag, int_prop_tag>::type
313 * static_assert(std::is_same_v<tag_type, int_prop_tag> == true);
315 * @tparam Tags First tag: tag to be casted, rest tags: candidates
317 template <typename... Tags> struct tag_cast;
320 * @brief base case of tag_cast, if nothing matches return @a Tag
322 * @tparam Tag Tag to be casted
323 * @tparam Others empty parameter pack
325 template <typename Tag, typename... Others> struct tag_cast<Tag, Others...> {
330 * @brief normal case of the tag cast
332 * @tparam Tag tag to be casted
333 * @tparam BaseTag candidates to cast the tag
334 * @tparam Others pending candidates to be compared
336 template <typename Tag, typename BaseTag, typename... Others>
337 struct tag_cast<Tag, BaseTag, Others...> {
338 using type = std::conditional_t<std::is_base_of<BaseTag, Tag>::value, BaseTag,
339 typename tag_cast<Tag, Others...>::type>;
343 * @brief property to string converter.
344 * This structure defines how to convert to convert from/to string
346 * @tparam Tag tag type for the converter
347 * @tparam DataType underlying datatype
349 template <typename Tag, typename DataType> struct str_converter {
352 * @brief convert underlying value to string
354 * @param value value to convert to string
355 * @return std::string string
357 static std::string to_string(const DataType &value);
360 * @brief convert string to underlying value
362 * @param value value to convert to string
363 * @return DataType converted type
365 static DataType from_string(const std::string &value);
369 * @brief str converter specialization for enum classes
371 * @tparam EnumInfo enum informations
373 template <typename EnumInfo>
374 struct str_converter<enum_class_prop_tag, EnumInfo> {
377 * @copydoc template <typename Tag, typename DataType> struct str_converter
379 static std::string to_string(const typename EnumInfo::Enum &value) {
380 constexpr auto size = EnumInfo::EnumList.size();
381 constexpr const auto data = std::data(EnumInfo::EnumList);
382 for (unsigned i = 0; i < size; ++i) {
383 if (data[i] == value) {
384 return EnumInfo::EnumStr[i];
387 throw std::invalid_argument("Cannot find value in the enum list");
391 * @copydoc template <typename Tag, typename DataType> struct str_converter
393 static typename EnumInfo::Enum from_string(const std::string &value) {
394 constexpr auto size = EnumInfo::EnumList.size();
395 constexpr const auto data = std::data(EnumInfo::EnumList);
396 for (unsigned i = 0; i < size; ++i) {
397 if (istrequal(EnumInfo::EnumStr[i], value.c_str())) {
401 throw std::invalid_argument("No matching enum for value: " + value);
406 * @brief str converter which serializes a pointer and returns back to a ptr
408 * @tparam DataType pointer type
410 template <typename DataType> struct str_converter<ptr_prop_tag, DataType> {
413 * @brief convert underlying value to string
415 * @param value value to convert to string
416 * @return std::string string
418 static std::string to_string(const DataType &value) {
419 std::ostringstream ss;
425 * @brief convert string to underlying value
427 * @param value value to convert to string
428 * @return DataType converted type
430 static DataType from_string(const std::string &value) {
431 std::stringstream ss(value);
432 uintptr_t addr = static_cast<uintptr_t>(std::stoull(value, 0, 16));
433 return reinterpret_cast<DataType>(addr);
438 * @copydoc template <typename Tag, typename DataType> struct str_converter
442 str_converter<str_prop_tag, std::string>::to_string(const std::string &value);
445 * @copydoc template <typename Tag, typename DataType> struct str_converter
449 str_converter<str_prop_tag, std::string>::from_string(const std::string &value);
452 * @copydoc template <typename Tag, typename DataType> struct str_converter
455 std::string str_converter<uint_prop_tag, unsigned int>::to_string(
456 const unsigned int &value);
459 * @copydoc template <typename Tag, typename DataType> struct str_converter
462 unsigned int str_converter<uint_prop_tag, unsigned int>::from_string(
463 const std::string &value);
466 * @copydoc template <typename Tag, typename DataType> struct str_converter
470 str_converter<size_t_prop_tag, size_t>::to_string(const size_t &value);
473 * @copydoc template <typename Tag, typename DataType> struct str_converter
477 str_converter<size_t_prop_tag, size_t>::from_string(const std::string &value);
480 * @copydoc template <typename Tag, typename DataType> struct str_converter
483 std::string str_converter<bool_prop_tag, bool>::to_string(const bool &value);
486 * @copydoc template <typename Tag, typename DataType> struct str_converter
489 bool str_converter<bool_prop_tag, bool>::from_string(const std::string &value);
492 * @copydoc template <typename Tag, typename DataType> struct str_converter
495 std::string str_converter<float_prop_tag, float>::to_string(const float &value);
498 * @copydoc template <typename Tag, typename DataType> struct str_converter
501 float str_converter<float_prop_tag, float>::from_string(
502 const std::string &value);
505 * @copydoc template <typename Tag, typename DataType> struct str_converter
509 str_converter<double_prop_tag, double>::to_string(const double &value);
512 * @copydoc template <typename Tag, typename DataType> struct str_converter
516 str_converter<double_prop_tag, double>::from_string(const std::string &value);
519 * @brief convert dispatcher (to string)
521 * @tparam T type to convert
522 * @param property property to convert
523 * @return std::string converted string
525 template <typename T> std::string to_string(const T &property) {
526 using info = prop_info<T>;
528 typename tag_cast<typename info::tag_type, int_prop_tag, uint_prop_tag,
529 dimension_prop_tag, float_prop_tag, str_prop_tag,
530 enum_class_prop_tag>::type;
531 using data_type = typename info::data_type;
533 if constexpr (std::is_same_v<tag_type, enum_class_prop_tag>) {
534 return str_converter<tag_type, decltype(T::enum_info_)>::to_string(
537 return str_converter<tag_type, data_type>::to_string(property.get());
542 * @brief to_string vector specialization
543 * @copydoc template <typename T> std::string to_string(const T &property)
545 template <typename T> std::string to_string(const std::vector<T> &property) {
546 std::stringstream ss;
547 auto last_iter = property.end() - 1;
548 for (auto iter = property.begin(); iter != last_iter; ++iter) {
549 ss << to_string(*iter) << ',';
551 ss << to_string(*last_iter);
557 * @brief to_string array specialization
558 * @copydoc template <typename T> std::string to_string(const T &property)
560 template <typename T, size_t sz>
561 static std::string to_string(const std::array<T, sz> &value) {
562 std::stringstream ss;
563 auto last_iter = value.end() - 1;
564 for (auto iter = value.begin(); iter != last_iter; ++iter) {
565 ss << to_string(*iter) << ',';
567 ss << to_string(*last_iter);
574 * @brief convert dispatcher (from string)
577 * @tparam T type to convert
578 * @param str string to convert
579 * @param[out] property property, converted type
581 template <typename T> void from_string(const std::string &str, T &property) {
582 using info = prop_info<T>;
584 typename tag_cast<typename info::tag_type, int_prop_tag, uint_prop_tag,
585 dimension_prop_tag, float_prop_tag, str_prop_tag,
586 enum_class_prop_tag>::type;
587 using data_type = typename info::data_type;
589 if constexpr (std::is_same_v<tag_type, enum_class_prop_tag>) {
591 str_converter<tag_type, decltype(T::enum_info_)>::from_string(str));
593 property.set(str_converter<tag_type, data_type>::from_string(str));
598 * @brief transform iternal data, this is to use with std::transform
600 * @param item item to transform
601 * @return DataType transformed result
603 template <typename T> static T from_string_helper_(const std::string &item) {
605 from_string(item, t);
609 static const std::regex reg_("\\s*\\,\\s*");
612 * @brief from_string array specialization
613 * @copydoc template <typename T> void from_string(const std::string &str, T
615 * @note array implies that the size is @b fixed so there will be a validation
618 template <typename T, size_t sz>
619 void from_string(const std::string &value, std::array<T, sz> &property) {
620 auto v = split(value, reg_);
621 NNTR_THROW_IF(v.size() != sz, std::invalid_argument)
622 << "size must match with array size, array size: " << sz
623 << " string: " << value;
625 std::transform(v.begin(), v.end(), property.begin(), from_string_helper_<T>);
629 * @brief from_string vector specialization
630 * @copydoc str_converter<Tag, DataType>::to_string(const DataType &value)
631 * @note vector implies that the size is @b not fixed so there shouldn't be any
635 template <typename T>
636 void from_string(const std::string &value, std::vector<T> &property) {
637 auto v = split(value, reg_);
640 property.reserve(v.size());
641 std::transform(v.begin(), v.end(), std::back_inserter(property),
642 from_string_helper_<T>);
644 /******** below section is for enumerations ***************/
646 * @brief Enumeration of Data Type for model & layer
648 struct TensorDataTypeInfo {
649 using Enum = nntrainer::TensorDim::DataType;
650 static constexpr std::initializer_list<Enum> EnumList = {Enum::FP16,
653 static constexpr const char *EnumStr[] = {"FP16", "FP32"};
659 * @brief Activation Enumeration Information
662 class TensorDataType final : public EnumProperty<TensorDataTypeInfo> {
664 using prop_tag = enum_class_prop_tag;
665 static constexpr const char *key = "tensor_type";
667 TensorDataTypeInfo::Enum value = TensorDataTypeInfo::Enum::FP32) {
673 * @brief model tensor type : NCHW or NHWC
676 class TensorFormat : public nntrainer::Property<std::string> {
678 static constexpr const char *key =
679 "tensor_format"; /**< unique key to access */
680 using prop_tag = str_prop_tag; /**< property type */
685 * @param value value to set, defaults to false
687 TensorFormat(const std::string &value = "NCHW") { set(value); };
691 } // namespace nntrainer
693 #endif // __BASE_PROPERTIES_H__