[props] Support enum properties
authorJihoon Lee <jhoon.it.lee@samsung.com>
Tue, 7 Sep 2021 06:21:39 +0000 (15:21 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Thu, 9 Sep 2021 05:03:05 +0000 (14:03 +0900)
This patch aims to support enum based properties, please refer to the
test to see how it works.

**Self evaluation:**
1. Build test: [X]Passed [ ]Failed [ ]Skipped
2. Run test: [X]Passed [ ]Failed [ ]Skipped

Signed-off-by: Jihoon Lee <jhoon.it.lee@samsung.com>
nntrainer/utils/base_properties.h
test/unittest/unittest_base_properties.cpp

index 2ddb37f..a186a6b 100644 (file)
@@ -261,6 +261,17 @@ private:
 };
 
 /**
+ * @brief enum property
+ *
+ * @tparam T underlying type info to query enum_info
+ */
+template <typename EnumInfo>
+class EnumProperty : public Property<typename EnumInfo::Enum> {
+public:
+  static EnumInfo enum_info_;
+};
+
+/**
  * @brief abstract class for positive integer
  *
  */
@@ -349,6 +360,43 @@ template <typename Tag, typename DataType> struct str_converter {
 };
 
 /**
+ * @brief str converter specialization for enum classes
+ *
+ * @tparam EnumInfo enum informations
+ */
+template <typename EnumInfo>
+struct str_converter<enum_class_prop_tag, EnumInfo> {
+
+  /**
+   * @copydoc template <typename Tag, typename DataType> struct str_converter
+   */
+  static std::string to_string(const typename EnumInfo::Enum &value) {
+    constexpr auto size = EnumInfo::EnumList.size();
+    constexpr const auto data = std::data(EnumInfo::EnumList);
+    for (unsigned i = 0; i < size; ++i) {
+      if (data[i] == value) {
+        return EnumInfo::EnumStr[i];
+      }
+    }
+    throw std::invalid_argument("Cannot find value in the enum list");
+  }
+
+  /**
+   * @copydoc template <typename Tag, typename DataType> struct str_converter
+   */
+  static typename EnumInfo::Enum from_string(const std::string &value) {
+    constexpr auto size = EnumInfo::EnumList.size();
+    constexpr const auto data = std::data(EnumInfo::EnumList);
+    for (unsigned i = 0; i < size; ++i) {
+      if (istrequal(EnumInfo::EnumStr[i], value.c_str())) {
+        return data[i];
+      }
+    }
+    throw std::invalid_argument("No matching enum for value: " + value);
+  }
+};
+
+/**
  * @brief str converter which serializes a pointer and returns back to a ptr
  *
  * @tparam DataType pointer type
@@ -458,10 +506,16 @@ template <typename T> std::string to_string(const T &property) {
   using info = prop_info<T>;
   using tag_type =
     typename tag_cast<typename info::tag_type, int_prop_tag, uint_prop_tag,
-                      dimension_prop_tag, float_prop_tag, str_prop_tag>::type;
+                      dimension_prop_tag, float_prop_tag, str_prop_tag,
+                      enum_class_prop_tag>::type;
   using data_type = typename info::data_type;
 
-  return str_converter<tag_type, data_type>::to_string(property.get());
+  if constexpr (std::is_same_v<tag_type, enum_class_prop_tag>) {
+    return str_converter<tag_type, decltype(T::enum_info_)>::to_string(
+      property.get());
+  } else {
+    return str_converter<tag_type, data_type>::to_string(property.get());
+  }
 }
 
 /**
@@ -508,10 +562,16 @@ template <typename T> void from_string(const std::string &str, T &property) {
   using info = prop_info<T>;
   using tag_type =
     typename tag_cast<typename info::tag_type, int_prop_tag, uint_prop_tag,
-                      dimension_prop_tag, float_prop_tag, str_prop_tag>::type;
+                      dimension_prop_tag, float_prop_tag, str_prop_tag,
+                      enum_class_prop_tag>::type;
   using data_type = typename info::data_type;
 
-  property.set(str_converter<tag_type, data_type>::from_string(str));
+  if constexpr (std::is_same_v<tag_type, enum_class_prop_tag>) {
+    property.set(
+      str_converter<tag_type, decltype(T::enum_info_)>::from_string(str));
+  } else {
+    property.set(str_converter<tag_type, data_type>::from_string(str));
+  }
 }
 
 /**
index e0ceec4..1e56bb9 100644 (file)
@@ -106,6 +106,38 @@ public:
   static constexpr const char *key = "ptr_banana";
   using prop_tag = nntrainer::ptr_prop_tag;
 };
+
+/**
+ * @brief Enuminformation of BananaType;
+ *
+ */
+struct BananaEnumInfo {
+  /**
+   * @brief underlying enum
+   *
+   */
+  enum class Enum {
+    Cavendish = 0,
+    Plantain = 1,
+    Manzano = 2,
+  };
+
+  static constexpr std::initializer_list<Enum> EnumList = {
+    Enum::Cavendish, Enum::Plantain, Enum::Manzano};
+
+  static constexpr const char *EnumStr[] = {"Cavendish", "Plantain", "Manzano"};
+};
+
+/**
+ * @brief Type of Banana (enum based)
+ *
+ */
+class BananaType : public nntrainer::EnumProperty<BananaEnumInfo> {
+public:
+  using prop_tag = nntrainer::enum_class_prop_tag;
+  static constexpr const char *key = "banana_type";
+};
+
 } // namespace
 
 TEST(BasicProperty, tagCast) {
@@ -245,6 +277,26 @@ TEST(BasicProperty, valid_p) {
     EXPECT_FLOAT_EQ(q.get(), 1.3245f);
   }
 
+  { /**< enum type test from_string -> get */
+    BananaType t;
+    nntrainer::from_string("CAVENDISH", t);
+    EXPECT_EQ(t.get(), BananaEnumInfo::Enum::Cavendish);
+    nntrainer::from_string("Plantain", t);
+    EXPECT_EQ(t.get(), BananaEnumInfo::Enum::Plantain);
+    nntrainer::from_string("manzano", t);
+    EXPECT_EQ(t.get(), BananaEnumInfo::Enum::Manzano);
+  }
+
+  { /**< enum type test set -> to_string */
+    BananaType t;
+    t.set(BananaEnumInfo::Enum::Cavendish);
+    EXPECT_EQ("Cavendish", nntrainer::to_string(t));
+    t.set(BananaEnumInfo::Enum::Plantain);
+    EXPECT_EQ("Plantain", nntrainer::to_string(t));
+    t.set(BananaEnumInfo::Enum::Manzano);
+    EXPECT_EQ("Manzano", nntrainer::to_string(t));
+  }
+
   { /**< from_string -> get / to_string, uint vector prop */
     std::vector<NumBanana> bananas;
     EXPECT_EQ(nntrainer::getPropKey(bananas), "num_banana");