message: template to check if convert_from_string exists

Add a SFINAE template to determine if convert_from_string
exists for a type.

Signed-off-by: Patrick Williams <patrick@stwcx.xyz>
Change-Id: I5202dacd50734e06a02955ed613e124d8fe0127f
diff --git a/include/sdbusplus/message/native_types.hpp b/include/sdbusplus/message/native_types.hpp
index 1f77914..2624d34 100644
--- a/include/sdbusplus/message/native_types.hpp
+++ b/include/sdbusplus/message/native_types.hpp
@@ -221,6 +221,26 @@
     return details::convert_to_string<T>::op(t);
 }
 
+namespace details
+{
+// SFINAE templates to determine if convert_from_string exists for a type.
+template <typename T>
+auto has_convert_from_string_helper(T)
+    -> decltype(convert_from_string<T>::op(std::declval<std::string>()),
+                std::true_type());
+auto has_convert_from_string_helper(...) -> std::false_type;
+
+template <typename T>
+struct has_convert_from_string :
+    decltype(has_convert_from_string_helper(std::declval<T>()))
+{};
+
+template <typename T>
+inline constexpr bool has_convert_from_string_v =
+    has_convert_from_string<T>::value;
+
+} // namespace details
+
 } // namespace message
 } // namespace sdbusplus
 
diff --git a/test/server/message_variant.cpp b/test/server/message_variant.cpp
index 4c3ea0e..2fa44b7 100644
--- a/test/server/message_variant.cpp
+++ b/test/server/message_variant.cpp
@@ -43,6 +43,15 @@
     }
 };
 
+static_assert(
+    sdbusplus::message::details::has_convert_from_string_v<TestIf::EnumOne>,
+    "EnumOne does not have convert_from_string!");
+static_assert(
+    sdbusplus::message::details::has_convert_from_string_v<TestIf::EnumTwo>,
+    "EnumTwo does not have convert_from_string!");
+static_assert(!sdbusplus::message::details::has_convert_from_string_v<size_t>,
+              "size_t unexpectedly has a convert_from_string!");
+
 TEST_F(Object, PlainEnumOne)
 {
     run_test<TestIf::EnumOne>(TestIf::EnumOne::OneA);