You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by pn...@apache.org on 2022/10/31 22:02:26 UTC

[celix] 01/01: Refactor Celix utils and framework C++ headers for C++14 support

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 <pe...@gmail.com>
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.
 [![codecov](https://codecov.io/gh/apache/celix/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/celix)
 [![Coverity Scan Build Status](https://scan.coverity.com/projects/6685/badge.svg)](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