This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/stringview_support_for_properties in repository https://gitbox.apache.org/repos/asf/celix.git
commit 63f13c5491fe37f79002d7a6b65a1ae2ae070af4 Author: Pepijn Noltes <[email protected]> AuthorDate: Sun Nov 28 17:01:07 2021 +0100 Updates C++ properties to support string_view if C++17 is used. --- libs/utils/gtest/CMakeLists.txt | 13 ++- libs/utils/gtest/src/CxxPropertiesTestSuite.cc | 59 +++++++++++- libs/utils/include/celix/Properties.h | 123 ++++++++++++++++++++++++- 3 files changed, 189 insertions(+), 6 deletions(-) diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index 6cb860d..0802214 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -54,7 +54,7 @@ target_compile_definitions(test_utils PRIVATE -DTEST_ZIP_LOCATION=\"${TEST_ZIP_F target_compile_definitions(test_utils PRIVATE -DTEST_A_FILE_LOCATION=\"${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt\") target_compile_definitions(test_utils PRIVATE -DTEST_A_DIR_LOCATION=\"${CMAKE_CURRENT_SOURCE_DIR}\") -####### linkig zip against test executable to text extract zip from data ############################################### +####### linking zip against test executable to text extract zip from data ############################################### if (UNIX AND NOT APPLE) target_link_libraries(test_utils PRIVATE -Wl,--format=binary -Wl,"test.zip" -Wl,--format=default) else () @@ -64,3 +64,14 @@ endif () add_test(NAME test_utils COMMAND test_utils) setup_target_for_coverage(test_utils SCAN_DIR ..) + +#Setting standard to C++11 and testing C++ Properties to ensure that this still support C++11. +#Note that celix Properties are used in the C++11 dependency manager and this be backwards compatible with Celix 2.2.1 +set(CMAKE_CXX_STANDARD 11) +add_executable(test_properties_with_cxx11 + src/CxxPropertiesTestSuite.cc +) +target_link_libraries(test_properties_with_cxx11 PRIVATE Celix::utils GTest::gtest GTest::gtest_main) +add_test(NAME test_properties_with_cxx11 COMMAND test_properties_with_cxx11) +setup_target_for_coverage(test_properties_with_cxx11 SCAN_DIR ..) + diff --git a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc index 8f151e4..060562b 100644 --- a/libs/utils/gtest/src/CxxPropertiesTestSuite.cc +++ b/libs/utils/gtest/src/CxxPropertiesTestSuite.cc @@ -87,4 +87,61 @@ TEST_F(CxxPropertiesTestSuite, testWrap) { EXPECT_EQ(1, celix_properties_size(props)); celix_properties_destroy(props); -} \ No newline at end of file +} + +#if __cplusplus >= 201703L //C++17 or higher +TEST_F(CxxPropertiesTestSuite, testStringView) { + constexpr std::string_view stringViewKey = "KEY1"; + constexpr std::string_view stringViewValue = "VALUE1"; + std::string stringKey{"KEY2"}; + std::string stringValue{"VALUE2"}; + const char* charKey = "KEY3"; + const char* charValue = "VALUE3"; + + { + //rule: I can create properties with initializer_list using std::string, const char* and string_view + celix::Properties props { + {charKey, charValue}, + {stringKey, stringValue}, + {stringViewKey, stringViewValue} + }; + EXPECT_EQ(props.size(), 3); + + //rule: I can use the subscript operator using string_view objects + constexpr std::string_view k = "KEY2"; + constexpr std::string_view v = "VALUE_NEW"; + std::string check = props[k]; + EXPECT_EQ(check, "VALUE2"); + props[k] = v; + check = props[k]; + EXPECT_EQ(check, v); + } + + { + //rule: I can use set/get with string_view + constexpr std::string_view key = "TEST_KEY"; + constexpr std::string_view value = "TEST_VALUE"; + + celix::Properties props{}; + + props.set(key, value); + EXPECT_EQ(value, props.get(key)); + props[key] = value; + EXPECT_EQ(value, props.get(key)); + EXPECT_EQ(1, props.size()); + + props.set(key, std::string{"string value"}); + EXPECT_EQ("string value", props.get(key)); + props.set(key, "string value"); + EXPECT_EQ("string value", props.get(key)); + EXPECT_EQ(1, props.size()); + + props.set(key, 1L); //long + EXPECT_EQ(1L, props.getAsLong(key, -1)); + props.set(key, 1.0); //double + EXPECT_EQ(1.0, props.getAsDouble(key, -1)); + props.set(key, true); //bool + EXPECT_EQ(true, props.getAsBool(key, false)); + } +} +#endif diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 7698e77..0d00cd2 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -103,14 +103,36 @@ namespace celix { class ValueRef { public: - ValueRef(std::shared_ptr<celix_properties_t> _props, std::string _key) : props{std::move(_props)}, key{std::move(_key)} {} + ValueRef(std::shared_ptr<celix_properties_t> _props, std::string _key) : props{std::move(_props)}, stringKey{std::move(_key)}, charKey{nullptr} {} + + ValueRef(std::shared_ptr<celix_properties_t> _props, const char* _key) : props{std::move(_props)}, stringKey{}, charKey{_key} {} + +#if __cplusplus >= 201703L //C++17 or higher + ValueRef& operator=(std::string_view value) { + if (charKey == nullptr) { + celix_properties_set(props.get(), stringKey.c_str(), value.data()); + } else { + celix_properties_set(props.get(), charKey, value.data()); + } + return *this; + } +#else ValueRef& operator=(const std::string& value) { - celix_properties_set(props.get(), key.c_str(), value.c_str()); + if (charKey == nullptr) { + celix_properties_set(props.get(), stringKey.c_str(), value.c_str()); + } else { + celix_properties_set(props.get(), charKey, value.c_str()); + } return *this; } +#endif [[nodiscard]] const char* getValue() const { - return celix_properties_get(props.get(), key.c_str(), nullptr); + if (charKey == nullptr) { + return celix_properties_get(props.get(), stringKey.c_str(), nullptr); + } else { + return celix_properties_get(props.get(), charKey, nullptr); + } } operator std::string() const { @@ -119,7 +141,8 @@ namespace celix { } private: std::shared_ptr<celix_properties_t> props; - std::string key; + std::string stringKey; + const char* charKey; }; @@ -138,11 +161,19 @@ namespace celix { Properties(const Properties& rhs) : cProps{celix_properties_copy(rhs.cProps.get()), [](celix_properties_t* p) { celix_properties_destroy(p); }} {} +#if __cplusplus >= 201703L //C++17 or higher + Properties(std::initializer_list<std::pair<std::string_view, std::string_view>> list) : cProps{celix_properties_create(), [](celix_properties_t* p) { celix_properties_destroy(p); }} { + for(auto &entry : list) { + celix_properties_set(cProps.get(), entry.first.data(), entry.second.data()); + } + } +#else Properties(std::initializer_list<std::pair<std::string, std::string>> list) : cProps{celix_properties_create(), [](celix_properties_t* p) { celix_properties_destroy(p); }} { for(auto &entry : list) { celix_properties_set(cProps.get(), entry.first.c_str(), entry.second.c_str()); } } +#endif /** * @brief Wraps C properties, but does not take ownership -> dtor will not destroy properties @@ -162,6 +193,21 @@ namespace celix { return cProps.get(); } +#if __cplusplus >= 201703L //C++17 or higher + /** + * @brief Get the value for a property key + */ + ValueRef operator[](std::string_view key) { + return ValueRef{cProps, key.data()}; + } + + /** + * @brief Get the value for a property key + */ + ValueRef operator[](std::string_view key) const { + return ValueRef{cProps, key.data()}; + } +#else /** * @brief Get the value for a property key */ @@ -175,6 +221,7 @@ namespace celix { ValueRef operator[](std::string key) const { return ValueRef{cProps, std::move(key)}; } +#endif /** * @brief begin iterator @@ -208,6 +255,73 @@ namespace celix { return iter; } +#if __cplusplus >= 201703L //C++17 or higher + /** + * @brief Get the value for a property key or return the defaultValue if the key does not exists. + */ + [[nodiscard]] std::string get(std::string_view key, std::string_view defaultValue = {}) const { + const char* found = celix_properties_get(cProps.get(), key.data(), nullptr); + return found == nullptr ? std::string{defaultValue} : std::string{found}; + } + + /** + * @brief Get the value as long for a property key or return the defaultValue if the key does not exists. + */ + [[nodiscard]] long getAsLong(std::string_view key, long defaultValue) const { + return celix_properties_getAsLong(cProps.get(), key.data(), defaultValue); + } + + /** + * @brief Get the value as double for a property key or return the defaultValue if the key does not exists. + */ + [[nodiscard]] double getAsDouble(std::string_view key, double defaultValue) const { + return celix_properties_getAsDouble(cProps.get(), key.data(), defaultValue); + } + + /** + * @brief Get the value as bool for a property key or return the defaultValue if the key does not exists. + */ + [[nodiscard]] bool getAsBool(std::string_view key, bool defaultValue) const { + return celix_properties_getAsBool(cProps.get(), key.data(), defaultValue); + } + + /** + * @brief Sets a property + */ + void set(std::string_view key, std::string_view value) { + celix_properties_set(cProps.get(), key.data(), value.data()); + } + + /** + * @brief Sets a property + */ + void set(std::string_view key, std::string value) { + celix_properties_set(cProps.get(), key.data(), value.data()); + } + + /** + * @brief Sets a property + */ + void set(std::string_view key, const char* value) { + celix_properties_set(cProps.get(), key.data(), value); + } + + /** + * @brief Sets a bool property + */ + void set(std::string_view key, bool value) { + celix_properties_setBool(cProps.get(), key.data(), value); + } + + /** + * @brief Sets a T property. Will use std::to_string to convert the value to string. + */ + template<typename T> + void set(std::string_view key, T value) { + using namespace std; + celix_properties_set(cProps.get(), key.data(), to_string(value).c_str()); + } +#else /** * @brief Get the value for a property key or return the defaultValue if the key does not exists. */ @@ -266,6 +380,7 @@ namespace celix { using namespace std; celix_properties_set(cProps.get(), key.c_str(), to_string(value).c_str()); } +#endif /** * @brief Returns the nr of properties.
