From d3cfaaf9b786049cecc85a4f374df99f1eb85723 Mon Sep 17 00:00:00 2001 From: Jihoon Lee Date: Fri, 9 Apr 2021 22:46:58 +0900 Subject: [PATCH] [Props] Implement load properties This patch implement `load_properties` which helps loading properties for a tuple of properties. Please note that `getKeyVal` is taken from `parse_util` because there is a build option issue with android build **Self evaluation:** 1. Build test: [X]Passed [ ]Failed [ ]Skipped 2. Run test: [X]Passed [ ]Failed [ ]Skipped Signed-off-by: Jihoon Lee --- nntrainer/utils/base_properties.h | 4 +- nntrainer/utils/node_exporter.cpp | 4 +- nntrainer/utils/node_exporter.h | 120 ++++++++++++++++++++++++---------- nntrainer/utils/parse_util.cpp | 17 +++-- nntrainer/utils/parse_util.h | 3 +- test/unittest/unittest_properties.cpp | 33 +++++++--- 6 files changed, 127 insertions(+), 54 deletions(-) diff --git a/nntrainer/utils/base_properties.h b/nntrainer/utils/base_properties.h index 8c025c1..814e6aa 100644 --- a/nntrainer/utils/base_properties.h +++ b/nntrainer/utils/base_properties.h @@ -277,8 +277,8 @@ template std::string to_string(const T &property) { * @brief convert dispatcher (from string) * * @tparam T type to convert - * @param str string to vert - * @param property property, converted type + * @param str string to convert + * @param[out] property property, converted type */ template void from_string(const std::string &str, T &property) { using tag_type = diff --git a/nntrainer/utils/node_exporter.cpp b/nntrainer/utils/node_exporter.cpp index eefba97..9236c9f 100644 --- a/nntrainer/utils/node_exporter.cpp +++ b/nntrainer/utils/node_exporter.cpp @@ -1,4 +1,3 @@ -#include // SPDX-License-Identifier: Apache-2.0 /** * Copyright (C) 2021 Jihoon Lee @@ -10,6 +9,9 @@ * @author Jihoon Lee * @bug No known bugs except for NYI items */ + +#include + namespace nntrainer { template <> diff --git a/nntrainer/utils/node_exporter.h b/nntrainer/utils/node_exporter.h index 072e7a3..0411be5 100644 --- a/nntrainer/utils/node_exporter.h +++ b/nntrainer/utils/node_exporter.h @@ -15,6 +15,7 @@ #include #include +#include #ifndef __NODE_EXPORTER_H__ #define __NODE_EXPORTER_H__ @@ -114,40 +115,6 @@ public: const T &get_result(); private: - /** - * @brief base case of iterate_prop, iterate_prop iterates the given tuple - * - * @tparam I size of tuple(automated) - * @tparam Callable generic lambda to be called during iteration - * @tparam Ts types from tuple - * @param c callable gerneric labmda - * @param tup tuple to be iterated - * @return void - */ - template - typename std::enable_if::type - iterate_prop(Callable &&c, const std::tuple &tup) { - // end of recursion; - } - - /** - * @brief base case of iterate_prop, iterate_prop iterates the given tuple - * - * @tparam I size of tuple(automated) - * @tparam Callable generic lambda to be called during iteration - * @tparam Ts types from tuple - * @param c callable gerneric labmda - * @param tup tuple to be iterated - * @return not used - */ - template - typename std::enable_if<(I < sizeof...(Ts)), void>::type - iterate_prop(Callable &&c, const std::tuple &tup) { - c(std::get(tup), I); - - iterate_prop(c, tup); - } - std::vector> stored_result; /**< stored result */ @@ -156,5 +123,90 @@ private: bool is_exported; /**< boolean to check if exported */ }; +/** + * @brief base case of iterate_prop, iterate_prop iterates the given tuple + * + * @tparam I size of tuple(automated) + * @tparam Callable generic lambda to be called during iteration + * @tparam Ts types from tuple + * @param c callable gerneric labmda + * @param tup tuple to be iterated + * @return void + */ +template +typename std::enable_if::type +iterate_prop(Callable &&c, const std::tuple &tup) { + // end of recursion; +} + +/** + * @brief base case of iterate_prop, iterate_prop iterates the given tuple + * + * @tparam I size of tuple(automated) + * @tparam Callable generic lambda to be called during iteration + * @tparam Ts types from tuple + * @param c callable gerneric labmda + * @param tup tuple to be iterated + * @return not used + */ +template +typename std::enable_if<(I < sizeof...(Ts)), void>::type +iterate_prop(Callable &&c, const std::tuple &tup) { + c(std::get(tup), I); + + iterate_prop(c, tup); +} + +/** + * @copydoc template +typename std::enable_if<(I < sizeof...(Ts)), void>::type iterate_prop(Callable +&&c, const std::tuple &tup) + */ +template +typename std::enable_if<(I < sizeof...(Ts)), void>::type +iterate_prop(Callable &&c, std::tuple &tup) { + c(std::get(tup), I); + + iterate_prop(c, tup); +} + +/** + * @brief load property from the api formatted string ({"key=value", + * "key1=value1"}) + * + * @tparam Ts prop type + * @param string_vector api formatted string; + * @param[out] props props to be iterated + * @return std::vector vector of string that is not used while + * setting the property + */ +template +std::vector +load_properties(const std::vector &string_vector, + std::tuple &props) { + + std::vector left = string_vector; + + auto callable = [&left](auto &&prop, size_t index) { + std::string prop_key = std::remove_reference_t::key; + + for (auto iter = left.begin(); iter < left.end(); ++iter) { + std::string key, value; + int status = getKeyValue(*iter, key, value); + NNTR_THROW_IF(status != ML_ERROR_NONE, std::invalid_argument) + << "parsing property failed, original format: " << *iter; + + if (istrequal(prop_key, key) == true) { + from_string(value, prop); + iter = left.erase(iter); + } + } + }; + + iterate_prop(callable, props); + + return left; +} + } // namespace nntrainer #endif // __NODE_EXPORTER_H__ diff --git a/nntrainer/utils/parse_util.cpp b/nntrainer/utils/parse_util.cpp index 34c6246..cf12d15 100644 --- a/nntrainer/utils/parse_util.cpp +++ b/nntrainer/utils/parse_util.cpp @@ -38,19 +38,23 @@ namespace nntrainer { -int getKeyValue(std::string &input_str, std::string &key, std::string &value) { +int getKeyValue(const std::string &input_str, std::string &key, + std::string &value) { int status = ML_ERROR_NONE; + auto input_trimmed = input_str; + std::vector list; static const std::regex words_regex("[^\\s=]+"); - input_str.erase(std::remove(input_str.begin(), input_str.end(), ' '), - input_str.end()); - auto words_begin = - std::sregex_iterator(input_str.begin(), input_str.end(), words_regex); + input_trimmed.erase( + std::remove(input_trimmed.begin(), input_trimmed.end(), ' '), + input_trimmed.end()); + auto words_begin = std::sregex_iterator(input_trimmed.begin(), + input_trimmed.end(), words_regex); auto words_end = std::sregex_iterator(); int nwords = std::distance(words_begin, words_end); if (nwords != 2) { ml_loge("Error: input string must be 'key = value' format, %s given", - input_str.c_str()); + input_trimmed.c_str()); return ML_ERROR_INVALID_PARAMETER; } @@ -60,6 +64,7 @@ int getKeyValue(std::string &input_str, std::string &key, std::string &value) { key = list[0]; value = list[1]; + return status; } diff --git a/nntrainer/utils/parse_util.h b/nntrainer/utils/parse_util.h index fb7dea9..ecdccbb 100644 --- a/nntrainer/utils/parse_util.h +++ b/nntrainer/utils/parse_util.h @@ -166,7 +166,8 @@ int setBoolean(bool &val, std::string str); * @retval #ML_ERROR_NONE Successful. * @retval #ML_ERROR_INVALID_PARAMETER invalid parameter. */ -int getKeyValue(std::string &input_str, std::string &key, std::string &value); +int getKeyValue(const std::string &input_str, std::string &key, + std::string &value); /** * @brief join vector of int to string with delimiter "," diff --git a/test/unittest/unittest_properties.cpp b/test/unittest/unittest_properties.cpp index cc32e36..b1ffd3b 100644 --- a/test/unittest/unittest_properties.cpp +++ b/test/unittest/unittest_properties.cpp @@ -121,6 +121,29 @@ TEST(BasicProperty, valid_p) { auto pair2 = std::pair("quality_banana", ""); EXPECT_EQ(result[1], pair2); } + + { /**< export from layer */ + auto layer = nntrainer::FullyConnectedLayer(1); + nntrainer::Exporter e; + layer.export_to(e); + + auto result = e.get_result(); + auto pair = std::pair("unit", "1"); + EXPECT_EQ(result[0], pair); + } + + { /**< load from layer */ + auto props = std::make_tuple(NumBanana(), QualityOfBanana()); + + auto v = + nntrainer::load_properties({"num_banana=2", "quality_banana=thisisgood", + "num_banana=42", "not_used=key"}, + props); + + EXPECT_EQ(v, std::vector{"not_used=key"}); + EXPECT_EQ(std::get<0>(props).get(), 42); + EXPECT_EQ(std::get<1>(props).get(), "thisisgood"); + } } TEST(BasicProperty, setNotValid_01_n) { @@ -167,16 +190,6 @@ TEST(Exporter, notExported_n) { std::invalid_argument); } -TEST(Exporter, exportFromLayer_p) { - auto layer = nntrainer::FullyConnectedLayer(1); - nntrainer::Exporter e; - layer.export_to(e); - - auto result = e.get_result(); - auto pair = std::pair("unit", "1"); - EXPECT_EQ(result[0], pair); -} - /** * @brief Main gtest */ -- 2.7.4