This is an automated email from the ASF dual-hosted git repository. pnoltes pushed a commit to branch feature/cxx14_framework_support in repository https://gitbox.apache.org/repos/asf/celix.git
commit 82cc3ffcd5ba7f5cf17a33c9211cdd7690c79881 Author: Pepijn Noltes <[email protected]> AuthorDate: Mon Oct 31 23:01:50 2022 +0100 Refactor Celix utils and framework C++ headers for C++14 support --- .github/workflows/macos-nightly.yml | 3 + .github/workflows/macos.yml | 2 + .github/workflows/ubuntu-nightly.yml | 4 + .github/workflows/ubuntu.yml | 2 + CMakeLists.txt | 2 + README.md | 4 +- libs/framework/gtest/CMakeLists.txt | 92 ++++++--- .../gtest/src/CxxBundleContextTestSuite.cc | 3 +- libs/framework/include/celix/Bundle.h | 32 +++- libs/framework/include/celix/BundleActivator.h | 8 +- libs/framework/include/celix/BundleContext.h | 143 ++++++++++++-- libs/framework/include/celix/ServiceRegistration.h | 128 ++++++++----- .../include/celix/ServiceRegistrationBuilder.h | 15 +- libs/framework/include/celix/TrackerBuilders.h | 13 +- libs/framework/include/celix/Trackers.h | 210 ++++++++++++++------- libs/framework/include/celix/UseServiceBuilder.h | 11 +- libs/utils/gtest/CMakeLists.txt | 29 +-- libs/utils/gtest/src/CxxUtilsTestSuite.cc | 18 +- libs/utils/include/celix/Filter.h | 44 ++++- libs/utils/include/celix/Properties.h | 21 ++- libs/utils/include/celix/Utils.h | 40 ++-- 21 files changed, 590 insertions(+), 234 deletions(-) diff --git a/.github/workflows/macos-nightly.yml b/.github/workflows/macos-nightly.yml index 8fdd6b98..6ca4191e 100644 --- a/.github/workflows/macos-nightly.yml +++ b/.github/workflows/macos-nightly.yml @@ -27,6 +27,9 @@ jobs: BUILD_OPTIONS: | -DENABLE_TESTING=ON -DENABLE_ADDRESS_SANITIZER=ON + -DBUILD_CXX_REMOTE_SERVICE_ADMIN=ON + -DCELIX_TEST_DEPENDENCY_MANAGER_FOR_CXX11=ON + -DCELIX_TEST_FOR_CXX14=ON run: | mkdir build install cd build diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 0517e80a..b14634ee 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -26,6 +26,8 @@ jobs: -DENABLE_TESTING=ON -DENABLE_ADDRESS_SANITIZER=ON -DBUILD_CXX_REMOTE_SERVICE_ADMIN=ON + -DCELIX_TEST_DEPENDENCY_MANAGER_FOR_CXX11=ON + -DCELIX_TEST_FOR_CXX14=ON run: | mkdir build install cd build diff --git a/.github/workflows/ubuntu-nightly.yml b/.github/workflows/ubuntu-nightly.yml index 6ce46e73..8a66c85d 100644 --- a/.github/workflows/ubuntu-nightly.yml +++ b/.github/workflows/ubuntu-nightly.yml @@ -56,6 +56,10 @@ jobs: CXX: ${{ matrix.cxx_compiler }} BUILD_OPTIONS: | -DENABLE_TESTING=ON + -DBUILD_EXPERIMENTAL=ON + -DBUILD_CXX_REMOTE_SERVICE_ADMIN=ON + -DCELIX_TEST_DEPENDENCY_MANAGER_FOR_CXX11=ON + -DCELIX_TEST_FOR_CXX14=ON BUILD_OPTIONS_SANITIZER: | -DENABLE_ADDRESS_SANITIZER=ON BUILD_OPTIONS_V3_API: | diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index b95e44e4..7b78569f 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -56,6 +56,8 @@ jobs: -DENABLE_TESTING=ON -DBUILD_EXPERIMENTAL=ON -DBUILD_CXX_REMOTE_SERVICE_ADMIN=ON + -DCELIX_TEST_DEPENDENCY_MANAGER_FOR_CXX11=ON + -DCELIX_TEST_FOR_CXX14=ON BUILD_OPTIONS_SANITIZER: | -DENABLE_ADDRESS_SANITIZER=ON BUILD_OPTIONS_V3_API: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 42b3097f..575c43e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,6 +187,8 @@ endif () option(CELIX_USE_ZIP_INSTEAD_OF_JAR "Default Celix cmake command will use jar to package bundle (if found). This option enforces Celix to use zip instead." OFF) option(CELIX_CXX "Build C++ libraries and bundles. Note for tests C++ is always used." ON) +option(CELIX_TEST_DEPENDENCY_MANAGER_FOR_CXX11 "Test the Dependency Manager for C++11 support" OFF) +option(CELIX_TEST_FOR_CXX14 "Test celix utils and framework C++ header for C++14 support" OFF) #Libraries and Launcher add_subdirectory(libs) diff --git a/README.md b/README.md index 46e42d15..5024b23b 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ limitations under the License. [](https://codecov.io/gh/apache/celix) [](https://scan.coverity.com/projects/6685) -Apache Celix is an implementation of a dynamic service framework inspired by the OSGi specification and adapted to C -and C++ (C++17). +Apache Celix is an implementation of a dynamic service framework inspired by the OSGi specification and adapted to C, +C++14 and C++17. It is a framework to develop dynamic modular software applications using component and in-process service-oriented programming. diff --git a/libs/framework/gtest/CMakeLists.txt b/libs/framework/gtest/CMakeLists.txt index 9de0023a..977fcf99 100644 --- a/libs/framework/gtest/CMakeLists.txt +++ b/libs/framework/gtest/CMakeLists.txt @@ -38,16 +38,19 @@ if(NOT APPLE) endif() add_dependencies(unresolvable_bundle sublib) -add_executable(test_framework - src/single_framework_test.cpp - src/multiple_frameworks_test.cpp - src/bundle_context_bundles_tests.cpp - src/bundle_context_services_test.cpp - src/DependencyManagerTestSuite.cc - src/CxxBundleContextTestSuite.cc - src/HelloWorldCxxActivator.cc - src/CxxFrameworkFactoryTestSuite.cc - src/CxxBundleActivatorTestSuite.cc) +set(CELIX_FRAMEWORK_TEST_SOURCES + src/single_framework_test.cpp + src/multiple_frameworks_test.cpp + src/bundle_context_bundles_tests.cpp + src/bundle_context_services_test.cpp + src/DependencyManagerTestSuite.cc + src/CxxBundleContextTestSuite.cc + src/HelloWorldCxxActivator.cc + src/CxxFrameworkFactoryTestSuite.cc + src/CxxBundleActivatorTestSuite.cc +) + +add_executable(test_framework ${CELIX_FRAMEWORK_TEST_SOURCES}) target_link_libraries(test_framework PRIVATE Celix::framework CURL::libcurl GTest::gtest GTest::gtest_main) add_celix_bundle_dependencies(test_framework simple_test_bundle1 @@ -93,21 +96,60 @@ target_compile_definitions(test_framework PRIVATE add_test(NAME test_framework COMMAND test_framework) setup_target_for_coverage(test_framework SCAN_DIR ..) -#Setting standard to C++11 and testing C++ dependency manager to ensure that this still support C++11. -#This ensure that the C++11 dependency manager is backwards compatible with Celix 2.2.1 -set(CMAKE_CXX_STANDARD 11) -add_executable(test_dep_man_with_cxx11 - src/DependencyManagerTestSuite.cc -) -target_link_libraries(test_dep_man_with_cxx11 PRIVATE Celix::framework CURL::libcurl GTest::gtest GTest::gtest_main) -target_compile_definitions(test_dep_man_with_cxx11 PRIVATE - SIMPLE_CXX_DEP_MAN_BUNDLE_LOC="${SIMPLE_CXX_DEP_MAN_BUNDLE_LOC}" -) -add_test(NAME test_dep_man_with_cxx11 COMMAND test_dep_man_with_cxx11) -setup_target_for_coverage(test_dep_man_with_cxx11 SCAN_DIR ..) +if (CELIX_TEST_DEPENDENCY_MANAGER_FOR_CXX11) + #Setting standard to C++11 and testing C++ dependency manager to ensure that this still support C++11. + #This ensure that the C++11 dependency manager is backwards compatible with Celix 2.2.1 + set(CMAKE_CXX_STANDARD 11) + add_executable(test_dep_man_with_cxx11 + src/DependencyManagerTestSuite.cc + ) + target_link_libraries(test_dep_man_with_cxx11 PRIVATE Celix::framework CURL::libcurl GTest::gtest GTest::gtest_main) + target_compile_definitions(test_dep_man_with_cxx11 PRIVATE + SIMPLE_CXX_DEP_MAN_BUNDLE_LOC="${SIMPLE_CXX_DEP_MAN_BUNDLE_LOC}" + ) + add_test(NAME test_dep_man_with_cxx11 COMMAND test_dep_man_with_cxx11) + setup_target_for_coverage(test_dep_man_with_cxx11 SCAN_DIR ..) + + #Also to ensure that CELIX_GEN_CXX_BUNDLE_ACTIVATOR still works for C++11 bundle activators with a + #dependency manager argument, the HelloWorldCxxActivatorWithDepMan will be used to create a C++11 bundle + add_celix_bundle(test_dep_man_bundle_activator_with_cxx11 SOURCES src/HelloWorldCxxActivatorWithDepMan.cc VERSION 1.0.0) +endif () + +if (CELIX_TEST_FOR_CXX14) + #Setting standard to C++14 and testing the C++ framework headers to ensure that C++14 is also supported. + set(CMAKE_CXX_STANDARD 14) + + add_executable(test_framework_with_cxx14 ${CELIX_FRAMEWORK_TEST_SOURCES}) + target_link_libraries(test_framework_with_cxx14 PRIVATE Celix::framework CURL::libcurl GTest::gtest GTest::gtest_main) + add_celix_bundle_dependencies(test_framework_with_cxx14 + simple_test_bundle1 + simple_test_bundle2 simple_test_bundle3 simple_test_bundle4 + simple_test_bundle5 bundle_with_exception unresolveable_bundle simple_cxx_bundle simple_cxx_dep_man_bundle cmp_test_bundle) + target_include_directories(test_framework_with_cxx14 PRIVATE ../src) + + #Also to ensure that CELIX_GEN_CXX_BUNDLE_ACTIVATOR still for C++11. + add_celix_bundle(simple_cxx_bundle_with_cxx1 SOURCES src/HelloWorldCxxActivator.cc VERSION 1.0.0) + add_celix_bundle(simple_cxx_dep_man_bundle_with_cxx1 SOURCES src/HelloWorldCxxActivatorWithDepMan.cc VERSION 1.0.0) + add_celix_bundle_dependencies(test_framework_with_cxx14 simple_cxx_bundle_with_cxx1 simple_cxx_dep_man_bundle_with_cxx1) -#Also to ensure that CELIX_GEN_CXX_BUNDLE_ACTIVATOR still works for C++11 bundle activators with a -#dependency manager argument, the HelloWorldCxxActivatorWithDepMan will be used to create a C++11 bundle + celix_get_bundle_file(simple_cxx_bundle_with_cxx1 SIMPLE_CXX_BUNDLE_WITH_CXX11_LOC) + celix_get_bundle_file(simple_cxx_dep_man_bundle_with_cxx1 SIMPLE_CXX_DEP_MAN_WITH_CXX11_BUNDLE_LOC) -add_celix_bundle(test_dep_man_bundle_activator_with_cxx11 SOURCES src/HelloWorldCxxActivatorWithDepMan.cc VERSION 1.0.0) + target_compile_definitions(test_framework_with_cxx14 PRIVATE + SIMPLE_TEST_BUNDLE1_LOCATION="${SIMPLE_TEST_BUNDLE1}" + SIMPLE_TEST_BUNDLE2_LOCATION="${SIMPLE_TEST_BUNDLE2}" + SIMPLE_TEST_BUNDLE3_LOCATION="${SIMPLE_TEST_BUNDLE3}" + SIMPLE_TEST_BUNDLE4_LOCATION="${SIMPLE_TEST_BUNDLE4_FILENAME}" + SIMPLE_TEST_BUNDLE5_LOCATION="${SIMPLE_TEST_BUNDLE5_FILENAME}" + TEST_BUNDLE_WITH_EXCEPTION_LOCATION="${BUNDLE_WITH_EXCEPTION}" + TEST_BUNDLE_UNRESOLVABLE_LOCATION="${UNRESOLVABLE_BUNDLE}" + SIMPLE_CXX_BUNDLE_LOC="${SIMPLE_CXX_BUNDLE_WITH_CXX11_LOC}" + CMP_TEST_BUNDLE_LOC="${CMP_TEST_BUNDLE_LOC}" + SIMPLE_CXX_DEP_MAN_BUNDLE_LOC="${SIMPLE_CXX_DEP_MAN_WITH_CXX11_BUNDLE_LOC}" + CMP_TEST_BUNDLE_LOC="${CMP_TEST_BUNDLE_LOC}" + INSTALL_AND_START_BUNDLES_CONFIG_PROPERTIES_FILE="${CMAKE_CURRENT_BINARY_DIR}/install_and_start_bundles.properties" + ) + add_test(NAME test_framework_with_cxx14 COMMAND test_framework_with_cxx14) + setup_target_for_coverage(test_framework_with_cxx14 SCAN_DIR ..) +endif () diff --git a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc index fe015185..dabc4288 100644 --- a/libs/framework/gtest/src/CxxBundleContextTestSuite.cc +++ b/libs/framework/gtest/src/CxxBundleContextTestSuite.cc @@ -715,6 +715,7 @@ TEST_F(CxxBundleContextTestSuite, GetBundleInformation) { EXPECT_TRUE(startCalled); } +#if __cplusplus >= 201703L //C++17 or higher class TestInterfaceWithStaticInfo { public: static constexpr std::string_view NAME = "TestName"; @@ -727,7 +728,7 @@ TEST_F(CxxBundleContextTestSuite, RegisterServiceWithNameAndVersionInfo) { EXPECT_EQ(reg->getServiceName(), "TestName"); EXPECT_EQ(reg->getServiceVersion(), "1.2.3"); } - +#endif TEST_F(CxxBundleContextTestSuite, listBundles) { auto list = ctx->listBundleIds(); diff --git a/libs/framework/include/celix/Bundle.h b/libs/framework/include/celix/Bundle.h index 90e31be4..4bc09ad8 100644 --- a/libs/framework/include/celix/Bundle.h +++ b/libs/framework/include/celix/Bundle.h @@ -64,25 +64,32 @@ namespace celix { * @param path The relative path to a bundle resource * @return The use-able entry path or an empty string if the entry is not found. */ +#if __cplusplus >= 201703L //C++17 or higher [[nodiscard]] std::string getEntry(std::string_view path) const { - std::string result{}; - char* entry = celix_bundle_getEntry(cBnd.get(), path.data()); - if (entry != nullptr) { - result = std::string{entry}; - free(entry); - } - return result; + return getEntryInternal(path.data()); + } +#else + std::string getEntry(const std::string& path) const { + return getEntryInternal(path.c_str()); } +#endif /** * @brief Get a manifest attribute value from the bundle manifest. * @param attribute The attribute to get the value from. * @return The attribute value or an empty string if the attribute is not present in the bundle manifest. */ +#if __cplusplus >= 201703L //C++17 or higher [[nodiscard]] std::string getManifestValue(std::string_view attribute) const { const char* header = celix_bundle_getManifestValue(cBnd.get(), attribute.data()); return header == nullptr ? std::string{} : std::string{header}; } +#else + [[nodiscard]] std::string getManifestValue(const std::string& attribute) const { + const char* header = celix_bundle_getManifestValue(cBnd.get(), attribute.c_str()); + return header == nullptr ? std::string{} : std::string{header}; + } +#endif /** * @brief the symbolic name of the bundle. @@ -143,6 +150,17 @@ namespace celix { return celix_bundle_isSystemBundle(cBnd.get()); } private: + std::string getEntryInternal(const char* path) const { + std::string result{}; + char* entry = celix_bundle_getEntry(cBnd.get(), path); + if (entry != nullptr) { + result = std::string{entry}; + free(entry); + } + return result; + } + + const std::shared_ptr<celix_bundle_t> cBnd; }; } diff --git a/libs/framework/include/celix/BundleActivator.h b/libs/framework/include/celix/BundleActivator.h index 267777c8..1656db26 100644 --- a/libs/framework/include/celix/BundleActivator.h +++ b/libs/framework/include/celix/BundleActivator.h @@ -24,10 +24,10 @@ #include "celix/dm/DependencyManager.h" #include "celix_bundle_activator.h" -#if __cplusplus >= 201703L //C++17 or higher +#if __cplusplus >= 201402L //C++14 or higher #include "celix/BundleContext.h" - -namespace celix::impl { +namespace celix { +namespace impl { template<typename I> struct BundleActivatorData { long bndId{}; @@ -94,7 +94,7 @@ namespace celix::impl { waitForExpired(bndId, ctx, "celix::dm::DependencyManager", dm); return CELIX_SUCCESS; } -} +}} #else //C++11 namespace celix { namespace impl { diff --git a/libs/framework/include/celix/BundleContext.h b/libs/framework/include/celix/BundleContext.h index bbbc8ffb..20af2000 100644 --- a/libs/framework/include/celix/BundleContext.h +++ b/libs/framework/include/celix/BundleContext.h @@ -78,11 +78,19 @@ namespace celix { * @param name The optional name of the service. If not provided celix::typeName<I> will be used to defer the service name. * @return A ServiceRegistrationBuilder object. */ +#if __cplusplus >= 201703L //C++17 or higher template<typename I, typename Implementer> ServiceRegistrationBuilder<I> registerService(std::shared_ptr<Implementer> implementer, std::string_view name = {}) { std::shared_ptr<I> svc = implementer; //note Implement should be derived from I return ServiceRegistrationBuilder<I>{cCtx, std::move(svc), celix::typeName<I>(name)}; } +#else + template<typename I, typename Implementer> + ServiceRegistrationBuilder<I> registerService(std::shared_ptr<Implementer> implementer, const std::string& name = {}) { + std::shared_ptr<I> svc = implementer; //note Implement should be derived from I + return ServiceRegistrationBuilder<I>{cCtx, std::move(svc), celix::typeName<I>(name)}; + } +#endif /** * @brief Register a (unmanaged) service in the Celix framework using a fluent builder API. @@ -94,11 +102,19 @@ namespace celix { * By default the service registration is configure to register the service async, but to unregister the * service sync (because the svc pointer is unmanaged). */ +#if __cplusplus >= 201703L //C++17 or higher template<typename I, typename Implementer> ServiceRegistrationBuilder<I> registerUnmanagedService(Implementer* svc, std::string_view name = {}) { auto unmanagedSvc = std::shared_ptr<I>{svc, [](I*){/*nop*/}}; return ServiceRegistrationBuilder<I>{cCtx, std::move(unmanagedSvc), celix::typeName<I>(name), true, false}; } +#else + template<typename I, typename Implementer> + ServiceRegistrationBuilder<I> registerUnmanagedService(Implementer* svc, const std::string& name = {}) { + auto unmanagedSvc = std::shared_ptr<I>{svc, [](I*){/*nop*/}}; + return ServiceRegistrationBuilder<I>{cCtx, std::move(unmanagedSvc), celix::typeName<I>(name), true, false}; + } +#endif //TODO registerServiceFactory<I>() @@ -128,10 +144,17 @@ namespace celix { * @param name The optional service name to use. If not provided celix::typeName<I> will be used to defer the service name. * @return A UseServiceBuilder object. */ +#if __cplusplus >= 201703L //C++17 or higher template<typename I> UseServiceBuilder<I> useService(std::string_view name = {}) { return UseServiceBuilder<I>{cCtx, celix::typeName<I>(name), true}; } +#else + template<typename I> + UseServiceBuilder<I> useService(const std::string& name = {}) { + return UseServiceBuilder<I>{cCtx, celix::typeName<I>(name), true}; + } +#endif /** * @brief Use services registered in the Celix framework using a fluent builder API. @@ -156,10 +179,17 @@ namespace celix { * @param name The optional service name to use. If not provided celix::typeName<I> will be used to defer the service name. * @return A UseServiceBuilder object. */ +#if __cplusplus >= 201703L //C++17 or higher template<typename I> UseServiceBuilder<I> useServices(std::string_view name = {}) { return UseServiceBuilder<I>{cCtx, celix::typeName<I>(name), false}; } +#else + template<typename I> + UseServiceBuilder<I> useServices(const std::string& name = {}) { + return UseServiceBuilder<I>{cCtx, celix::typeName<I>(name), false}; + } +#endif /** * @brief Finds the highest ranking service using the optional provided (LDAP) filter @@ -172,10 +202,17 @@ namespace celix { * @param versionRange An optional version range. * @return The service id of the found service or -1 if the service was not found. */ +#if __cplusplus >= 201703L //C++17 or higher template<typename I> long findService(std::string_view filter = {}, std::string_view versionRange = {}) { return findServiceWithName(celix::typeName<I>(), filter, versionRange); } +#else + template<typename I> + long findService(const std::string& filter = {}, const std::string& versionRange = {}) { + return findServiceWithName(celix::typeName<I>(), filter, versionRange); + } +#endif /** * @brief Finds the highest ranking service using the provided service name and @@ -186,6 +223,7 @@ namespace celix { * @param versionRange An optional version range. * @return The service id of the found service or -1 if the service was not found. */ +#if __cplusplus >= 201703L //C++17 or higher long findServiceWithName(std::string_view name, std::string_view filter = {}, std::string_view versionRange = {}) { waitIfAbleForEvents(); celix_service_filter_options_t opts{}; @@ -194,6 +232,16 @@ namespace celix { opts.versionRange = versionRange.empty() ? nullptr : versionRange.data(); return celix_bundleContext_findServiceWithOptions(cCtx.get(), &opts); } +#else + long findServiceWithName(const std::string& name, const std::string& filter = {}, const std::string& versionRange = {}) { + waitIfAbleForEvents(); + celix_service_filter_options_t opts{}; + opts.serviceName = name.empty() ? nullptr : name.data(); + opts.filter = filter.empty() ? nullptr : filter.data(); + opts.versionRange = versionRange.empty() ? nullptr : versionRange.data(); + return celix_bundleContext_findServiceWithOptions(cCtx.get(), &opts); + } +#endif /** * @brief Finds all services matching the optional provided (LDAP) filter @@ -206,10 +254,17 @@ namespace celix { * @param versionRange An optional version range. * @return A vector of service ids. */ +#if __cplusplus >= 201703L //C++17 or higher template<typename I> std::vector<long> findServices(std::string_view filter = {}, std::string_view versionRange = {}) { return findServicesWithName(celix::typeName<I>(), filter, versionRange); } +#else + template<typename I> + std::vector<long> findServices(const std::string& filter = {}, const std::string& versionRange = {}) { + return findServicesWithName(celix::typeName<I>(), filter, versionRange); + } +#endif /** * @brief Finds all service matching the provided service name and the optional (LDAP) filter @@ -220,22 +275,21 @@ namespace celix { * @param versionRange An optional version range. * @return A vector of service ids. */ +#if __cplusplus >= 201703L //C++17 or higher std::vector<long> findServicesWithName(std::string_view name, std::string_view filter = {}, std::string_view versionRange = {}) { - waitIfAbleForEvents(); - celix_service_filter_options_t opts{}; - opts.serviceName = name.empty() ? nullptr : name.data(); - opts.filter = filter.empty() ? nullptr : filter.data(); - opts.versionRange = versionRange.empty() ? nullptr : versionRange.data(); - - std::vector<long> result{}; - auto cList = celix_bundleContext_findServicesWithOptions(cCtx.get(), &opts); - for (int i = 0; i < celix_arrayList_size(cList); ++i) { - long svcId = celix_arrayList_getLong(cList, i); - result.push_back(svcId); - } - celix_arrayList_destroy(cList); - return result; + return findServicesWithNameInternal( + name.empty() ? nullptr : name.data(), + filter.empty() ? nullptr : filter.data(), + versionRange.empty() ? nullptr : versionRange.data()); + } +#else + std::vector<long> findServicesWithName(const std::string& name, const std::string& filter = {}, const std::string& versionRange = {}) { + return findServicesWithNameInternal( + name.empty() ? nullptr : name.c_str(), + filter.empty() ? nullptr : filter.c_str(), + versionRange.empty() ? nullptr : versionRange.c_str()); } +#endif /** * @brief Track services in the Celix framework using a fluent builder API. @@ -257,10 +311,17 @@ namespace celix { * @param name The optional service name. If empty celix::typeName<I> will be used to defer the service name. * @return A ServiceTrackerBuilder object. */ +#if __cplusplus >= 201703L //C++17 or higher template<typename I> ServiceTrackerBuilder<I> trackServices(std::string_view name = {}) { return ServiceTrackerBuilder<I>{cCtx, celix::typeName<I>(name)}; } +#else + template<typename I> + ServiceTrackerBuilder<I> trackServices(const std::string& name = {}) { + return ServiceTrackerBuilder<I>{cCtx, celix::typeName<I>(name)}; + } +#endif /** * @brief Track services in the Celix framework using a fluent builder API. @@ -310,10 +371,17 @@ namespace celix { * @param name The optional service name. If empty celix::typeName<I> will be used to defer the service name. * @return A MetaTrackerBuilder object. */ +#if __cplusplus >= 201703L //C++17 or higher template<typename I> MetaTrackerBuilder trackServiceTrackers(std::string_view name = {}) { return MetaTrackerBuilder(cCtx, celix::typeName<I>(name)); } +#else + template<typename I> + MetaTrackerBuilder trackServiceTrackers(const std::string& name = {}) { + return MetaTrackerBuilder(cCtx, celix::typeName<I>(name)); + } +#endif /** * @brief Track service trackers in the Celix framework using a fluent builder API. @@ -333,9 +401,15 @@ namespace celix { * @param autoStart If the bundle should also be started. * @return the bundleId (>= 0) or < 0 if the bundle could not be installed and possibly started. */ +#if __cplusplus >= 201703L //C++17 or higher long installBundle(std::string_view bndLocation, bool autoStart = true) { return celix_bundleContext_installBundle(cCtx.get(), bndLocation.data(), autoStart); } +#else + long installBundle(const std::string& bndLocation, bool autoStart = true) { + return celix_bundleContext_installBundle(cCtx.get(), bndLocation.c_str(), autoStart); + } +#endif /** * @brief Uninstall the bundle with the provided bundle id. @@ -405,9 +479,15 @@ namespace celix { * @param defaultVal The default value to use if the property is not found. * @return The config property value for the provided key or the provided defaultValue is the name is not found. */ +#if __cplusplus >= 201703L //C++17 or higher [[nodiscard]] std::string getConfigProperty(std::string_view name, std::string_view defaultValue) const { return std::string{celix_bundleContext_getProperty(cCtx.get(), name.data(), defaultValue.data())}; } +#else + std::string getConfigProperty(const std::string& name, const std::string& defaultValue) const { + return std::string{celix_bundleContext_getProperty(cCtx.get(), name.c_str(), defaultValue.c_str())}; + } +#endif /** * @brief Gets the config property for the provided name and returns it as a long. @@ -421,9 +501,15 @@ namespace celix { * @return The config property value (as long) for the provided key or the provided defaultValue is the name * is not found or not a valid long. */ +#if __cplusplus >= 201703L //C++17 or higher [[nodiscard]] long getConfigPropertyAsLong(std::string_view name, long defaultValue) const { return celix_bundleContext_getPropertyAsLong(cCtx.get(), name.data(), defaultValue); } +#else + long getConfigPropertyAsLong(const std::string& name, long defaultValue) const { + return celix_bundleContext_getPropertyAsLong(cCtx.get(), name.c_str(), defaultValue); + } +#endif /** * @brief Gets the config property for the provided name and returns it as a double. @@ -437,9 +523,15 @@ namespace celix { * @return The config property value (as double) for the provided key or the provided defaultValue is the name * is not found or not a valid double. */ +#if __cplusplus >= 201703L //C++17 or higher [[nodiscard]] double getConfigPropertyAsDouble(std::string_view name, double defaultValue) const { return celix_bundleContext_getPropertyAsDouble(cCtx.get(), name.data(), defaultValue); } +#else + double getConfigPropertyAsDouble(const std::string& name, double defaultValue) const { + return celix_bundleContext_getPropertyAsDouble(cCtx.get(), name.c_str(), defaultValue); + } +#endif /** * @brief Gets the config property for the provided name and returns it as a bool. @@ -455,9 +547,15 @@ namespace celix { * @return The config property value (as boolean) for the provided key or the provided defaultValue is the name * is not found or not a valid boolean. */ +#if __cplusplus >= 201703L //C++17 or higher [[nodiscard]] long getConfigPropertyAsBool(std::string_view name, bool defaultValue) const { return celix_bundleContext_getPropertyAsBool(cCtx.get(), name.data(), defaultValue); } +#else + long getConfigPropertyAsBool(const std::string& name, bool defaultValue) const { + return celix_bundleContext_getPropertyAsBool(cCtx.get(), name.c_str(), defaultValue); + } +#endif /** * @brief Get the bundle of this bundle context. @@ -619,6 +717,23 @@ namespace celix { return result; } + std::vector<long> findServicesWithNameInternal(const char* name, const char* filter, const char* versionRange) { + waitIfAbleForEvents(); + celix_service_filter_options_t opts{}; + opts.serviceName = name; + opts.filter = filter; + opts.versionRange = versionRange; + + std::vector<long> result{}; + auto cList = celix_bundleContext_findServicesWithOptions(cCtx.get(), &opts); + for (int i = 0; i < celix_arrayList_size(cList); ++i) { + long svcId = celix_arrayList_getLong(cList, i); + result.push_back(svcId); + } + celix_arrayList_destroy(cList); + return result; + } + const std::shared_ptr<celix_bundle_context_t> cCtx; const std::shared_ptr<celix::dm::DependencyManager> dm; const Bundle bnd; diff --git a/libs/framework/include/celix/ServiceRegistration.h b/libs/framework/include/celix/ServiceRegistration.h index 3b077365..6f7b4f0a 100644 --- a/libs/framework/include/celix/ServiceRegistration.h +++ b/libs/framework/include/celix/ServiceRegistration.h @@ -67,6 +67,7 @@ namespace celix { * @return The new ServiceRegistration object as shared ptr. * @throws celix::Exception */ +#if __cplusplus >= 201703L //C++17 or higher static std::shared_ptr<ServiceRegistration> create(std::shared_ptr<celix_bundle_context_t> cCtx, std::shared_ptr<void> svc, std::string_view name, @@ -76,54 +77,25 @@ namespace celix { bool unregisterAsync, std::vector<std::function<void(ServiceRegistration&)>> onRegisteredCallbacks, std::vector<std::function<void(ServiceRegistration&)>> onUnregisteredCallbacks) { - auto delCallback = [](ServiceRegistration* reg) { - if (reg->getState() == ServiceRegistrationState::UNREGISTERED) { - delete reg; - } else { - /* - * if not registered/unregistering -> unregister() -> new event on the Celix event thread - * if unregistering -> nop unregister() -> there is already a event on the Celix event thread to unregister - */ - reg->unregister(); - - /* - * Creating event on the Event loop, this will be after the unregistration is done - */ - auto* fw = celix_bundleContext_getFramework(reg->cCtx.get()); - auto* bnd = celix_bundleContext_getBundle(reg->cCtx.get()); - long bndId = celix_bundle_getId(bnd); - celix_framework_fireGenericEvent( - fw, - -1, - bndId, - "celix::ServiceRegistration delete callback", - reg, - [](void *data) { - auto* r = static_cast<ServiceRegistration*>(data); - delete r; - }, - nullptr, - nullptr); - } - }; - - auto reg = std::shared_ptr<ServiceRegistration>{ - new ServiceRegistration{ - std::move(cCtx), - std::move(svc), - name, - version, - std::move(properties), - registerAsync, - unregisterAsync, - std::move(onRegisteredCallbacks), - std::move(onUnregisteredCallbacks)}, - delCallback - }; - reg->setSelf(reg); - reg->registerService(); - return reg; + return createInternal(std::move(cCtx), std::move(svc), name.data(), + version.data(), std::move(properties), registerAsync, + unregisterAsync, std::move(onRegisteredCallbacks), std::move(onUnregisteredCallbacks)); } +#else + static std::shared_ptr<ServiceRegistration> create(std::shared_ptr<celix_bundle_context_t> cCtx, + std::shared_ptr<void> svc, + const std::string& name, + const std::string& version, + celix::Properties properties, + bool registerAsync, + bool unregisterAsync, + std::vector<std::function<void(ServiceRegistration&)>> onRegisteredCallbacks, + std::vector<std::function<void(ServiceRegistration&)>> onUnregisteredCallbacks) { + return createInternal(std::move(cCtx), std::move(svc), name.c_str(), + version.c_str(), std::move(properties), registerAsync, + unregisterAsync, std::move(onRegisteredCallbacks), std::move(onUnregisteredCallbacks)); + } +#endif /** * @brief The service name for this service registration. @@ -256,8 +228,8 @@ namespace celix { ServiceRegistration( std::shared_ptr<celix_bundle_context_t> _cCtx, std::shared_ptr<void> _svc, - std::string_view _name, - std::string_view _version, + const char* _name, + const char* _version, celix::Properties _properties, bool _registerAsync, bool _unregisterAsync, @@ -273,6 +245,64 @@ namespace celix { onUnregisteredCallbacks{std::move(_onUnregisteredCallbacks)}, svc{std::move(_svc)} {} + static std::shared_ptr<ServiceRegistration> createInternal( + std::shared_ptr<celix_bundle_context_t> cCtx, + std::shared_ptr<void> svc, + const char* name, + const char* version, + celix::Properties properties, + bool registerAsync, + bool unregisterAsync, + std::vector<std::function<void(ServiceRegistration&)>> onRegisteredCallbacks, + std::vector<std::function<void(ServiceRegistration&)>> onUnregisteredCallbacks) { + auto delCallback = [](ServiceRegistration* reg) { + if (reg->getState() == ServiceRegistrationState::UNREGISTERED) { + delete reg; + } else { + /* + * if not registered/unregistering -> unregister() -> new event on the Celix event thread + * if unregistering -> nop unregister() -> there is already a event on the Celix event thread to unregister + */ + reg->unregister(); + + /* + * Creating event on the Event loop, this will be after the unregistration is done + */ + auto* fw = celix_bundleContext_getFramework(reg->cCtx.get()); + auto* bnd = celix_bundleContext_getBundle(reg->cCtx.get()); + long bndId = celix_bundle_getId(bnd); + celix_framework_fireGenericEvent( + fw, + -1, + bndId, + "celix::ServiceRegistration delete callback", + reg, + [](void *data) { + auto* r = static_cast<ServiceRegistration*>(data); + delete r; + }, + nullptr, + nullptr); + } + }; + + auto reg = std::shared_ptr<ServiceRegistration>{ + new ServiceRegistration{ + std::move(cCtx), + std::move(svc), + name, + version, + std::move(properties), + registerAsync, + unregisterAsync, + std::move(onRegisteredCallbacks), + std::move(onUnregisteredCallbacks)}, + delCallback + }; + reg->setSelf(reg); + reg->registerService(); + return reg; + } /** * @brief Register service in the Celix framework. diff --git a/libs/framework/include/celix/ServiceRegistrationBuilder.h b/libs/framework/include/celix/ServiceRegistrationBuilder.h index 54bbd0e7..a05de383 100644 --- a/libs/framework/include/celix/ServiceRegistrationBuilder.h +++ b/libs/framework/include/celix/ServiceRegistrationBuilder.h @@ -45,15 +45,15 @@ namespace celix { ServiceRegistrationBuilder( std::shared_ptr<celix_bundle_context_t> _cCtx, std::shared_ptr<I> _svc, - std::string_view _name, + std::string _name, bool _registerAsync = true, bool _unregisterAsync = true) : cCtx{std::move(_cCtx)}, svc{std::move(_svc)}, - name{_name}, + name{std::move(_name)}, version{celix::typeVersion<I>()}, registerAsync{_registerAsync}, - unregisterAsync{_unregisterAsync}{} + unregisterAsync{_unregisterAsync} {} ServiceRegistrationBuilder& operator=(ServiceRegistrationBuilder&&) = delete; ServiceRegistrationBuilder(const ServiceRegistrationBuilder&) = delete; @@ -64,15 +64,24 @@ namespace celix { * * This will lead to a 'service.version' service property. */ +#if __cplusplus >= 201703L //C++17 or higher ServiceRegistrationBuilder& setVersion(std::string_view v) { version = v; return *this; } +#else + ServiceRegistrationBuilder& setVersion(std::string v) { version = std::move(v); return *this; } +#endif /** * @brief Add a property to the service properties. * * If a key is already present the value will be overridden. */ +#if __cplusplus >= 201703L //C++17 or higher template<typename T> ServiceRegistrationBuilder& addProperty(std::string_view key, T&& value) { properties.template set(key, std::forward<T>(value)); return *this; } +#else + template<typename T> + ServiceRegistrationBuilder& addProperty(const std::string& key, T&& value) { properties.template set(key, std::forward<T>(value)); return *this; } +#endif /** * @brief Set the service properties. diff --git a/libs/framework/include/celix/TrackerBuilders.h b/libs/framework/include/celix/TrackerBuilders.h index bec73086..35883d82 100644 --- a/libs/framework/include/celix/TrackerBuilders.h +++ b/libs/framework/include/celix/TrackerBuilders.h @@ -42,9 +42,9 @@ namespace celix { //NOTE private to prevent move so that a build() call cannot be forgotten ServiceTrackerBuilder(ServiceTrackerBuilder&&) noexcept = default; public: - explicit ServiceTrackerBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string_view _name) : + explicit ServiceTrackerBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _name) : cCtx{std::move(_cCtx)}, - name{_name} {} + name{std::move(_name)} {} ServiceTrackerBuilder& operator=(ServiceTrackerBuilder&&) = delete; ServiceTrackerBuilder(const ServiceTrackerBuilder&) = delete; @@ -57,7 +57,11 @@ namespace celix { * Example: * "(property_key=value)" */ +#if __cplusplus >= 201703L //C++17 or higher ServiceTrackerBuilder& setFilter(std::string_view f) { filter = celix::Filter{f}; return *this; } +#else + ServiceTrackerBuilder& setFilter(const std::string& f) { filter = celix::Filter{f}; return *this; } +#endif /** * @brief Set filter to be used to matching services. @@ -310,10 +314,9 @@ namespace celix { //NOTE private to prevent move so that a build() call cannot be forgotten MetaTrackerBuilder(MetaTrackerBuilder &&) = default; public: - explicit MetaTrackerBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string_view _serviceName) : + explicit MetaTrackerBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _serviceName) : cCtx{std::move(_cCtx)}, - serviceName{_serviceName} - {} + serviceName{std::move(_serviceName)} {} MetaTrackerBuilder &operator=(MetaTrackerBuilder &&) = delete; MetaTrackerBuilder(const MetaTrackerBuilder &) = delete; diff --git a/libs/framework/include/celix/Trackers.h b/libs/framework/include/celix/Trackers.h index 4a2b491e..cd99ecf3 100644 --- a/libs/framework/include/celix/Trackers.h +++ b/libs/framework/include/celix/Trackers.h @@ -217,18 +217,19 @@ namespace celix { */ class GenericServiceTracker : public AbstractTracker { public: +#if __cplusplus >= 201703L //C++17 or higher GenericServiceTracker(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string_view _svcName, std::string_view _svcVersionRange, celix::Filter _filter) : AbstractTracker{std::move(_cCtx)}, svcName{_svcName}, svcVersionRange{_svcVersionRange}, filter{std::move(_filter)} { - opts.trackerCreatedCallbackData = this; - opts.trackerCreatedCallback = [](void *data) { - auto* trk = static_cast<GenericServiceTracker*>(data); - { - std::lock_guard<std::mutex> callbackLock{trk->mutex}; - trk->state = TrackerState::OPEN; - } - }; + setupServiceTrackerOptions(); + } +#else + GenericServiceTracker(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _svcName, + std::string _svcVersionRange, celix::Filter _filter) : AbstractTracker{std::move(_cCtx)}, svcName{std::move(_svcName)}, + svcVersionRange{std::move(_svcVersionRange)}, filter{std::move(_filter)} { + setupServiceTrackerOptions(); } +#endif ~GenericServiceTracker() override = default; @@ -278,6 +279,18 @@ namespace celix { const celix::Filter filter; celix_service_tracking_options opts{}; //note only set in the ctor std::atomic<size_t> svcCount{0}; + + private: + void setupServiceTrackerOptions() { + opts.trackerCreatedCallbackData = this; + opts.trackerCreatedCallback = [](void *data) { + auto* trk = static_cast<GenericServiceTracker*>(data); + { + std::lock_guard<std::mutex> callbackLock{trk->mutex}; + trk->state = TrackerState::OPEN; + } + }; + } }; /** @@ -307,6 +320,7 @@ namespace celix { * @return The new service tracker as shared ptr. * @throws celix::Exception */ +#if __cplusplus >= 201703L //C++17 or higher static std::shared_ptr<ServiceTracker<I>> create( std::shared_ptr<celix_bundle_context_t> cCtx, std::string_view svcName, @@ -315,7 +329,6 @@ namespace celix { std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> setCallbacks, std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> addCallbacks, std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> remCallbacks) { - auto tracker = std::shared_ptr<ServiceTracker<I>>{ new ServiceTracker<I>{ std::move(cCtx), @@ -329,6 +342,29 @@ namespace celix { tracker->open(); return tracker; } +#else + static std::shared_ptr<ServiceTracker<I>> create( + std::shared_ptr<celix_bundle_context_t> cCtx, + std::string svcName, + std::string svcVersionRange, + celix::Filter filter, + std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> setCallbacks, + std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> addCallbacks, + std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> remCallbacks) { + auto tracker = std::shared_ptr<ServiceTracker<I>>{ + new ServiceTracker<I>{ + std::move(cCtx), + std::move(svcName), + std::move(svcVersionRange), + std::move(filter), + std::move(setCallbacks), + std::move(addCallbacks), + std::move(remCallbacks)}, + AbstractTracker::delCallback<ServiceTracker<I>>()}; + tracker->open(); + return tracker; + } +#endif /** * @brief Get the current highest ranking service tracked by this tracker. @@ -382,6 +418,7 @@ namespace celix { std::shared_ptr<const celix::Bundle> owner; }; +#if __cplusplus >= 201703L //C++17 or higher ServiceTracker(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string_view _svcName, std::string_view _svcVersionRange, celix::Filter _filter, std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> _setCallbacks, @@ -391,63 +428,21 @@ namespace celix { setCallbacks{std::move(_setCallbacks)}, addCallbacks{std::move(_addCallbacks)}, remCallbacks{std::move(_remCallbacks)} { - opts.filter.serviceName = svcName.empty() ? nullptr : svcName.c_str(); - opts.filter.versionRange = svcVersionRange.empty() ? nullptr : svcVersionRange.c_str(); - opts.filter.filter = filter.empty() ? nullptr : filter.getFilterCString(); - opts.callbackHandle = this; - opts.addWithOwner = [](void *handle, void *voidSvc, const celix_properties_t* cProps, const celix_bundle_t* cBnd) { - auto tracker = static_cast<ServiceTracker<I>*>(handle); - auto entry = createEntry(voidSvc, cProps, cBnd); - { - std::lock_guard<std::mutex> lck{tracker->mutex}; - tracker->entries.insert(entry); - tracker->cachedEntries[entry->svcId] = entry; - } - tracker->svcCount.fetch_add(1, std::memory_order_relaxed); - for (const auto& cb : tracker->addCallbacks) { - cb(entry->svc, entry->properties, entry->owner); - } - tracker->invokeUpdateCallbacks(); - }; - opts.removeWithOwner = [](void *handle, void*, const celix_properties_t* cProps, const celix_bundle_t*) { - auto tracker = static_cast<ServiceTracker<I>*>(handle); - long svcId = celix_properties_getAsLong(cProps, OSGI_FRAMEWORK_SERVICE_ID, -1L); - std::shared_ptr<SvcEntry> entry{}; - { - std::lock_guard<std::mutex> lck{tracker->mutex}; - auto it = tracker->cachedEntries.find(svcId); - assert(it != tracker->cachedEntries.end()); //should not happen, added during add callback - entry = it->second; - tracker->cachedEntries.erase(it); - tracker->entries.erase(entry); - } - for (const auto& cb : tracker->remCallbacks) { - cb(entry->svc, entry->properties, entry->owner); - } - tracker->invokeUpdateCallbacks(); - tracker->svcCount.fetch_sub(1, std::memory_order_relaxed); - tracker->waitForExpiredSvcEntry(entry); - }; - opts.setWithOwner = [](void *handle, void *voidSvc, const celix_properties_t *cProps, const celix_bundle_t *cBnd) { - auto tracker = static_cast<ServiceTracker<I>*>(handle); - std::lock_guard<std::mutex> lck{tracker->mutex}; - auto prevEntry = tracker->highestRankingServiceEntry; - if (voidSvc) { - tracker->highestRankingServiceEntry = createEntry(voidSvc, cProps, cBnd); - } else { - tracker->highestRankingServiceEntry = nullptr; - } - for (const auto& cb : tracker->setCallbacks) { - if (tracker->highestRankingServiceEntry) { - auto& e = tracker->highestRankingServiceEntry; - cb(e->svc, e->properties, e->owner); - } else /*"unset"*/ { - cb(nullptr, nullptr, nullptr); - } - } - tracker->waitForExpiredSvcEntry(prevEntry); - }; + setupServiceTrackerOptions(); + } +#else + ServiceTracker(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _svcName, + std::string _svcVersionRange, celix::Filter _filter, + std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> _setCallbacks, + std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> _addCallbacks, + std::vector<std::function<void(const std::shared_ptr<I>&, const std::shared_ptr<const celix::Properties>&, const std::shared_ptr<const celix::Bundle>&)>> _remCallbacks) : + GenericServiceTracker{std::move(_cCtx), std::move(_svcName), std::move(_svcVersionRange), std::move(_filter)}, + setCallbacks{std::move(_setCallbacks)}, + addCallbacks{std::move(_addCallbacks)}, + remCallbacks{std::move(_remCallbacks)} { + setupServiceTrackerOptions(); } +#endif static std::shared_ptr<SvcEntry> createEntry(void* voidSvc, const celix_properties_t* cProps, const celix_bundle_t* cBnd) { long svcId = celix_properties_getAsLong(cProps, OSGI_FRAMEWORK_SERVICE_ID, -1L); @@ -550,6 +545,66 @@ namespace celix { std::set<std::shared_ptr<SvcEntry>, SvcEntryCompare> entries{}; std::unordered_map<long, std::shared_ptr<SvcEntry>> cachedEntries{}; std::shared_ptr<SvcEntry> highestRankingServiceEntry{}; + + private: + void setupServiceTrackerOptions() { + opts.filter.serviceName = svcName.empty() ? nullptr : svcName.c_str(); + opts.filter.versionRange = svcVersionRange.empty() ? nullptr : svcVersionRange.c_str(); + opts.filter.filter = filter.empty() ? nullptr : filter.getFilterCString(); + opts.callbackHandle = this; + opts.addWithOwner = [](void *handle, void *voidSvc, const celix_properties_t* cProps, const celix_bundle_t* cBnd) { + auto tracker = static_cast<ServiceTracker<I>*>(handle); + auto entry = createEntry(voidSvc, cProps, cBnd); + { + std::lock_guard<std::mutex> lck{tracker->mutex}; + tracker->entries.insert(entry); + tracker->cachedEntries[entry->svcId] = entry; + } + tracker->svcCount.fetch_add(1, std::memory_order_relaxed); + for (const auto& cb : tracker->addCallbacks) { + cb(entry->svc, entry->properties, entry->owner); + } + tracker->invokeUpdateCallbacks(); + }; + opts.removeWithOwner = [](void *handle, void*, const celix_properties_t* cProps, const celix_bundle_t*) { + auto tracker = static_cast<ServiceTracker<I>*>(handle); + long svcId = celix_properties_getAsLong(cProps, OSGI_FRAMEWORK_SERVICE_ID, -1L); + std::shared_ptr<SvcEntry> entry{}; + { + std::lock_guard<std::mutex> lck{tracker->mutex}; + auto it = tracker->cachedEntries.find(svcId); + assert(it != tracker->cachedEntries.end()); //should not happen, added during add callback + entry = it->second; + tracker->cachedEntries.erase(it); + tracker->entries.erase(entry); + } + for (const auto& cb : tracker->remCallbacks) { + cb(entry->svc, entry->properties, entry->owner); + } + tracker->invokeUpdateCallbacks(); + tracker->svcCount.fetch_sub(1, std::memory_order_relaxed); + tracker->waitForExpiredSvcEntry(entry); + }; + opts.setWithOwner = [](void *handle, void *voidSvc, const celix_properties_t *cProps, const celix_bundle_t *cBnd) { + auto tracker = static_cast<ServiceTracker<I>*>(handle); + std::lock_guard<std::mutex> lck{tracker->mutex}; + auto prevEntry = tracker->highestRankingServiceEntry; + if (voidSvc) { + tracker->highestRankingServiceEntry = createEntry(voidSvc, cProps, cBnd); + } else { + tracker->highestRankingServiceEntry = nullptr; + } + for (const auto& cb : tracker->setCallbacks) { + if (tracker->highestRankingServiceEntry) { + auto& e = tracker->highestRankingServiceEntry; + cb(e->svc, e->properties, e->owner); + } else /*"unset"*/ { + cb(nullptr, nullptr, nullptr); + } + } + tracker->waitForExpiredSvcEntry(prevEntry); + }; + } }; /** @@ -695,22 +750,39 @@ namespace celix { * @return The new meta tracker as shared ptr. * @throws celix::Exception. */ +#if __cplusplus >= 201703L //C++17 or higher static std::shared_ptr<MetaTracker> create( std::shared_ptr<celix_bundle_context_t> cCtx, std::string_view serviceName, std::vector<std::function<void(const ServiceTrackerInfo&)>> onTrackerCreated, std::vector<std::function<void(const ServiceTrackerInfo&)>> onTrackerDestroyed) { - auto tracker = std::shared_ptr<MetaTracker>{ new MetaTracker{ std::move(cCtx), - serviceName, + std::string{serviceName}, + std::move(onTrackerCreated), + std::move(onTrackerDestroyed)}, + AbstractTracker::delCallback<MetaTracker>()}; + tracker->open(); + return tracker; + } +#else + static std::shared_ptr<MetaTracker> create( + std::shared_ptr<celix_bundle_context_t> cCtx, + std::string serviceName, + std::vector<std::function<void(const ServiceTrackerInfo&)>> onTrackerCreated, + std::vector<std::function<void(const ServiceTrackerInfo&)>> onTrackerDestroyed) { + auto tracker = std::shared_ptr<MetaTracker>{ + new MetaTracker{ + std::move(cCtx), + std::move(serviceName), std::move(onTrackerCreated), std::move(onTrackerDestroyed)}, AbstractTracker::delCallback<MetaTracker>()}; tracker->open(); return tracker; } +#endif /** * @see AbstractTracker::open @@ -756,11 +828,11 @@ namespace celix { private: MetaTracker( std::shared_ptr<celix_bundle_context_t> _cCtx, - std::string_view _serviceName, + std::string _serviceName, std::vector<std::function<void(const ServiceTrackerInfo&)>> _onTrackerCreated, std::vector<std::function<void(const ServiceTrackerInfo&)>> _onTrackerDestroyed) : AbstractTracker{std::move(_cCtx)}, - serviceName{_serviceName}, + serviceName{std::move(_serviceName)}, onTrackerCreated{std::move(_onTrackerCreated)}, onTrackerDestroyed{std::move(_onTrackerDestroyed)} {} diff --git a/libs/framework/include/celix/UseServiceBuilder.h b/libs/framework/include/celix/UseServiceBuilder.h index f6c6d11b..6aa8aa24 100644 --- a/libs/framework/include/celix/UseServiceBuilder.h +++ b/libs/framework/include/celix/UseServiceBuilder.h @@ -54,11 +54,10 @@ namespace celix { //NOTE private to prevent move so that a build() call cannot be forgotten UseServiceBuilder(UseServiceBuilder&&) noexcept = default; public: - explicit UseServiceBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string_view _name, bool _useSingleService = true) : + explicit UseServiceBuilder(std::shared_ptr<celix_bundle_context_t> _cCtx, std::string _name, bool _useSingleService = true) : cCtx{std::move(_cCtx)}, - name{_name}, - useSingleService{_useSingleService} { - } + name{std::move(_name)}, + useSingleService{_useSingleService} {} UseServiceBuilder& operator=(UseServiceBuilder&&) = delete; UseServiceBuilder(const UseServiceBuilder&) = delete; @@ -71,7 +70,11 @@ namespace celix { * Example: * "(property_key=value)" */ +#if __cplusplus >= 201703L //C++17 or higher UseServiceBuilder& setFilter(std::string_view f) { filter = celix::Filter{f}; return *this; } +#else + UseServiceBuilder& setFilter(const std::string& f) { filter = celix::Filter{f}; return *this; } +#endif /** * @brief Set filter to be used to matching services. diff --git a/libs/utils/gtest/CMakeLists.txt b/libs/utils/gtest/CMakeLists.txt index b021b326..466002dc 100644 --- a/libs/utils/gtest/CMakeLists.txt +++ b/libs/utils/gtest/CMakeLists.txt @@ -15,6 +15,11 @@ # specific language governing permissions and limitations # under the License. +set(CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS + src/CxxUtilsTestSuite.cc + src/CxxPropertiesTestSuite.cc + src/CxxFilterTestSuite.cc +) add_executable(test_utils src/LogUtilsTestSuite.cc @@ -23,9 +28,7 @@ add_executable(test_utils src/HashMapTestSuite.cc src/ArrayListTestSuite.cc src/FileUtilsTestSuite.cc - src/CxxUtilsTestSuite.cc - src/CxxPropertiesTestSuite.cc - src/CxxFilterTestSuite.cc + ${CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS} ) target_link_libraries(test_utils PRIVATE Celix::utils GTest::gtest GTest::gtest_main) @@ -66,14 +69,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 ..) +if (CELIX_TEST_FOR_CXX14) + #Setting standard to C++14 and testing C++ Properties.h, Filter.h and Utils.h to ensure that C++14 is supported. + set(CMAKE_CXX_STANDARD 14) + add_executable(test_utils_cxx_headers_with_cxx14 + ${CELIX_UTIL_TEST_SOURCES_FOR_CXX_HEADERS} + ) + target_link_libraries(test_utils_cxx_headers_with_cxx14 PRIVATE Celix::utils GTest::gtest GTest::gtest_main) + add_test(NAME test_utils_cxx_headers_with_cxx14 COMMAND test_utils_cxx_headers_with_cxx14) + setup_target_for_coverage(test_utils_cxx_headers_with_cxx14 SCAN_DIR ..) +endif () diff --git a/libs/utils/gtest/src/CxxUtilsTestSuite.cc b/libs/utils/gtest/src/CxxUtilsTestSuite.cc index 11f21e99..4a051043 100644 --- a/libs/utils/gtest/src/CxxUtilsTestSuite.cc +++ b/libs/utils/gtest/src/CxxUtilsTestSuite.cc @@ -32,11 +32,13 @@ namespace example { std::string VERSION; //dummy non-static VERSION member, should not impact the typeVersion call }; +#if __cplusplus >= 201703L //C++17 or higher class TestType2 { public: static constexpr std::string_view NAME = "AnotherTestTypeName"; static constexpr const char * const VERSION = "1.2.0"; }; +#endif class TestType3 { public: @@ -52,20 +54,24 @@ TEST_F(CxxUtilsTestSuite, testTypeName) { auto name = celix::typeName<example::TestType>(); EXPECT_FALSE(name.empty()); +#if __cplusplus >= 201703L //C++17 or higher //When inferring a type name with no provided name, but the type has a NAME static member, - //the call should return the string value of the NAME static member + //the call should return the string value of the NAME static member (only support if C++17 is used). name = celix::typeName<example::TestType2>(); EXPECT_EQ(std::string{name}, std::string{"AnotherTestTypeName"}); +#endif //When inferring a type name with a provided name and the type does not have a NAME static member, //the call should return the provided name. name = celix::typeName<example::TestType>("OverrideName"); EXPECT_EQ(name, std::string{"OverrideName"}); +#if __cplusplus >= 201703L //C++17 or higher //When inferring a type name with a provided name and the type also has a NAME static member, - //the call should return the provided name. + //the call should return the provided name (only support if C++17 is used). name = celix::typeName<example::TestType2>("OverrideName"); EXPECT_EQ(name, std::string{"OverrideName"}); +#endif //When inferring a type name, where there is a static NAME member but not of the right type (constructable from string), //the call should return an inferred name based on __PRETTY_FUNCTION__ (see celix::impl::extractTypeName) @@ -80,20 +86,24 @@ TEST_F(CxxUtilsTestSuite, testTypeVersion) { auto version = celix::typeVersion<example::TestType>(); EXPECT_TRUE(version.empty()); +#if __cplusplus >= 201703L //C++17 or higher //When inferring a type version with no provided version and the type has a VERSION static member, - //the call should return the value of the static member VERSION. + //the call should return the value of the static member VERSION (only support if C++17 is used). version = celix::typeVersion<example::TestType2>(); EXPECT_EQ(std::string{version}, std::string{"1.2.0"}); +#endif //When inferring a type version with a provided version and the type does not have a VERSION static member, //the call should return the provided version. version = celix::typeVersion<example::TestType>("2.2.2"); EXPECT_EQ(version, std::string{"2.2.2"}); +#if __cplusplus >= 201703L //C++17 or higher //When inferring a type version with a provided version and the type does have a VERSION static member, - //the call should return the provided version. + //the call should return the provided version (only support if C++17 is used). version = celix::typeVersion<example::TestType2>("2.2.2"); EXPECT_EQ(version, std::string{"2.2.2"}); +#endif } TEST_F(CxxUtilsTestSuite, testSplit) { diff --git a/libs/utils/include/celix/Filter.h b/libs/utils/include/celix/Filter.h index ab00ec94..b92890d9 100644 --- a/libs/utils/include/celix/Filter.h +++ b/libs/utils/include/celix/Filter.h @@ -59,16 +59,21 @@ namespace celix { class Filter { public: Filter() : cFilter{createFilter("")} {} - explicit Filter(std::string_view filterStr) : cFilter{createFilter(filterStr)} {} +#if __cplusplus >= 201703L //C++17 or higher + explicit Filter(std::string_view filterStr) : cFilter{createFilter(filterStr.data())} {} +#else + explicit Filter(const std::string& filterStr) : cFilter{createFilter(filterStr.c_str())} {} +#endif + Filter(Filter&&) = default; Filter& operator=(Filter&&) = default; - Filter(const Filter& rhs) : cFilter{createFilter(rhs.getFilterString())} {} + Filter(const Filter& rhs) : cFilter{createFilter(rhs.getFilterString().c_str())} {} Filter& operator=(const Filter& rhs) { if (this != &rhs) { - cFilter = createFilter(rhs.getFilterString()); + cFilter = createFilter(rhs.getFilterString().c_str()); } return *this; } @@ -107,17 +112,30 @@ namespace celix { * @brief Find the attribute based on the provided key. * @return The found attribute value or an empty string if the attribute was not found. */ +#if __cplusplus >= 201703L //C++17 or higher [[nodiscard]] std::string findAttribute(std::string_view attributeKey) const { auto* cValue = celix_filter_findAttribute(cFilter.get(), attributeKey.data()); return cValue == nullptr ? std::string{} : std::string{cValue}; } +#else + std::string findAttribute(const std::string& attributeKey) const { + auto* cValue = celix_filter_findAttribute(cFilter.get(), attributeKey.data()); + return cValue == nullptr ? std::string{} : std::string{cValue}; + } +#endif /** * @brief Check whether the filter has a attribute with the provided attribute key. */ +#if __cplusplus >= 201703L //C++17 or higher [[nodiscard]] bool hasAttribute(std::string_view attributeKey) const { return celix_filter_findAttribute(cFilter.get(), attributeKey.data()) != nullptr; } +#else + bool hasAttribute(const std::string& attributeKey) const { + return celix_filter_findAttribute(cFilter.get(), attributeKey.data()) != nullptr; + } +#endif /** * @brief Check whether the filter indicates the mandatory presence of an attribute with a specific value for the provided attribute key. @@ -128,12 +146,18 @@ namespace celix { * using this method for attribute key "key1" on filter "(key1>=value1)" yields false. * using this method for attribute key "key1" on filter "(|(key1=value1)(key2=value2))" yields false. */ +#if __cplusplus >= 201703L //C++17 or higher [[nodiscard]] bool hasMandatoryEqualsValueAttribute(std::string_view attributeKey) const { return celix_filter_hasMandatoryEqualsValueAttribute(cFilter.get(), attributeKey.data()); } +#else + bool hasMandatoryEqualsValueAttribute(const std::string& attributeKey) const { + return celix_filter_hasMandatoryEqualsValueAttribute(cFilter.get(), attributeKey.c_str()); + } +#endif /** - * @brief Chek whether the filter indicates the mandatory absence of an attribute, regardless of its value, for the provided attribute key. + * @brief Check whether the filter indicates the mandatory absence of an attribute, regardless of its value, for the provided attribute key. * * example: * using this function for attribute key "key1" on the filter "(!(key1=*))" yields true. @@ -141,9 +165,15 @@ namespace celix { * using this function for attribute key "key1" on the filter "(key1=value)" yields false. * using this function for attribute key "key1" on the filter "(|(!(key1=*))(key2=value2))" yields false. */ +#if __cplusplus >= 201703L //C++17 or higher [[nodiscard]] bool hasMandatoryNegatedPresenceAttribute(std::string_view attributeKey) const { return celix_filter_hasMandatoryNegatedPresenceAttribute(cFilter.get(), attributeKey.data()); } +#else + bool hasMandatoryNegatedPresenceAttribute(const std::string& attributeKey) const { + return celix_filter_hasMandatoryNegatedPresenceAttribute(cFilter.get(), attributeKey.data()); + } +#endif /** * @brief Get the underlining C filter object. @@ -163,11 +193,11 @@ namespace celix { } private: - static std::shared_ptr<celix_filter_t> createFilter(std::string_view filterStr) { - if (filterStr.empty()) { + static std::shared_ptr<celix_filter_t> createFilter(const char* filterStr) { + if (filterStr == nullptr || strnlen(filterStr, 1) == 0) { return nullptr; } - auto* cf = celix_filter_create(filterStr.data()); + auto* cf = celix_filter_create(filterStr); if (cf == nullptr) { throw celix::FilterException{"Invalid LDAP filter '" + std::string{filterStr} + "'"}; } diff --git a/libs/utils/include/celix/Properties.h b/libs/utils/include/celix/Properties.h index 1504dab0..6af04f82 100644 --- a/libs/utils/include/celix/Properties.h +++ b/libs/utils/include/celix/Properties.h @@ -296,6 +296,9 @@ namespace celix { return celix_properties_getAsBool(cProps.get(), key.data(), defaultValue); } + /** + * @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) { if constexpr (std::is_same_v<std::decay_t<T>, bool>) { @@ -347,13 +350,6 @@ namespace celix { celix_properties_set(cProps.get(), key.c_str(), value.c_str()); } - /** - * @brief Sets a property - */ - void set(const std::string& key, const char* value) { - celix_properties_set(cProps.get(), key.c_str(), value); - } - /** * @brief Sets a bool property */ @@ -362,13 +358,22 @@ namespace celix { } /** - * @brief Sets a T property. Will use std::to_string to convert the value to string. + * @brief Sets a T property. Will use (std::) to_string to convert the value to string. */ template<typename T> void set(const std::string& key, T value) { using namespace std; celix_properties_set(cProps.get(), key.c_str(), to_string(value).c_str()); } + + /** + * @brief Sets a const char* property. + */ + template<> + void set<const char*>(const std::string& key, const char* value) { + using namespace std; + celix_properties_set(cProps.get(), key.c_str(), value); + } #endif /** diff --git a/libs/utils/include/celix/Utils.h b/libs/utils/include/celix/Utils.h index 5a72e36a..8eb25f61 100644 --- a/libs/utils/include/celix/Utils.h +++ b/libs/utils/include/celix/Utils.h @@ -83,6 +83,27 @@ namespace impl { } } +namespace celix { + /** + * @brief Splits a string using the provided delim. + * + * Also trims the entries from whitespaces. + * @param str The string to split + * @param delimiter The delimiter to use (default ",") + */ + inline std::vector<std::string> split(const std::string& str, const std::string& delimiter = ",") { + std::vector<std::string> result{}; + std::string delimiters = std::string{delimiter} + " \t"; + size_t found; + size_t pos = 0; + while ((found = str.find_first_not_of(delimiters, pos)) != std::string::npos) { + pos = str.find_first_of(delimiters, found); + result.emplace_back(str.substr(found, pos - found)); + } + return result; + } +} + #if __cplusplus >= 201703L //C++17 or higher namespace celix::impl { @@ -194,25 +215,6 @@ namespace celix { return ""; } } - - /** - * @brief Splits a string using the provided delim. - * - * Also trims the entries from whitespaces. - * @param str The string to split - * @param delimiter The delimiter to use (default ",") - */ - inline std::vector<std::string> split(std::string_view str, std::string_view delimiter = ",") { - std::vector<std::string> result{}; - std::string delimiters = std::string{delimiter} + " \t"; - size_t found; - size_t pos = 0; - while ((found = str.find_first_not_of(delimiters, pos)) != std::string::npos) { - pos = str.find_first_of(delimiters, found); - result.emplace_back(str.substr(found, pos - found)); - } - return result; - } } #else //Assuming at least C++11
