You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@celix.apache.org by oi...@apache.org on 2020/08/17 20:48:08 UTC

[celix] branch master updated: Feature/cpp17 promise (#272)

This is an automated email from the ASF dual-hosted git repository.

oipo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/celix.git


The following commit(s) were added to refs/heads/master by this push:
     new efee9f2  Feature/cpp17 promise (#272)
efee9f2 is described below

commit efee9f2d16413de73fa094cbe29dd29d65840a35
Author: Michael de Lang <ki...@gmail.com>
AuthorDate: Mon Aug 17 22:47:58 2020 +0200

    Feature/cpp17 promise (#272)
    
    Use C++17 as much as possible for promises
---
 CMakeLists.txt                                     |   8 +-
 misc/experimental/promise/CMakeLists.txt           |   6 +-
 misc/experimental/promise/api/celix/Deferred.h     |  25 +-
 misc/experimental/promise/api/celix/Promise.h      |  77 ++--
 .../promise/api/celix/PromiseFactory.h             |  10 +-
 .../promise/api/celix/PromiseInvocationException.h |   6 +-
 .../promise/api/celix/impl/SharedPromiseState.h    | 418 +++++++++++----------
 .../promise/gtest/src/PromiseTestSuite.cc          | 114 +++++-
 .../promise/gtest/src/VoidPromiseTestSuite.cc      |  15 +-
 misc/experimental/promise/src/PromiseExamples.cc   |   2 +-
 10 files changed, 423 insertions(+), 258 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9ede833..75fd9bd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -69,8 +69,12 @@ if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
 endif()
 
 if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
-    set(CMAKE_C_FLAGS "-Wno-unused-result -Wno-format-truncation -Wno-stringop-truncation -Wno-stringop-overflow ${CMAKE_C_FLAGS}")
-    set(CMAKE_CXX_FLAGS "-Wno-unused-result -Wno-format-truncation -Wno-stringop-truncation -Wno-stringop-overflow ${CMAKE_CXX_FLAGS}")
+    set(CMAKE_C_FLAGS "-Wno-unused-result -Wno-format-truncation -Wno-stringop-overflow ${CMAKE_C_FLAGS}")
+    set(CMAKE_CXX_FLAGS "-Wno-unused-result -Wno-format-truncation -Wno-stringop-overflow ${CMAKE_CXX_FLAGS}")
+    if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 8.0)
+        set(CMAKE_C_FLAGS "-Wno-stringop-truncation ${CMAKE_C_FLAGS}")
+        set(CMAKE_CXX_FLAGS "-Wno-stringop-truncation ${CMAKE_CXX_FLAGS}")
+    endif()
 endif()
 
 if (ENABLE_MORE_WARNINGS)
diff --git a/misc/experimental/promise/CMakeLists.txt b/misc/experimental/promise/CMakeLists.txt
index c4ccac9..2d547a4 100644
--- a/misc/experimental/promise/CMakeLists.txt
+++ b/misc/experimental/promise/CMakeLists.txt
@@ -28,7 +28,7 @@ if (NOT COMMAND celix_subproject)
 
     include(GNUInstallDirs)
 
-    set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
+    set(CMAKE_CXX_FLAGS "-std=c++17 ${CMAKE_CXX_FLAGS}")
     set(CMAKE_CXX_FLAGS_DEBUG "-g -DDEBUG ${CMAKE_CXX_FLAGS_DEBUG}")
 
     set(PROMISE_STANDALONE ON)
@@ -52,7 +52,7 @@ if (PROMISE OR PROMISE_STANDALONE)
         $<INSTALL_INTERFACE:include/celix/promise>
     )
     target_link_libraries(Promise INTERFACE TBB::tbb Threads::Threads)
-    target_compile_options(Promise INTERFACE -frtti) #Note needed for TBB
+    target_compile_options(Promise INTERFACE -frtti -std=c++17) #Note -frtti needed for TBB
     add_library(Celix::Promise ALIAS Promise)
 
     add_executable(PromiseExamples src/PromiseExamples.cc)
@@ -73,4 +73,4 @@ if (PROMISE OR PROMISE_STANDALONE)
                 DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CelixPromise" COMPONENT cmake)
     endif ()
 
-endif ()
\ No newline at end of file
+endif ()
diff --git a/misc/experimental/promise/api/celix/Deferred.h b/misc/experimental/promise/api/celix/Deferred.h
index 85ef15c..711620d 100644
--- a/misc/experimental/promise/api/celix/Deferred.h
+++ b/misc/experimental/promise/api/celix/Deferred.h
@@ -27,8 +27,18 @@
 
 #include <tbb/task.h>
 #include <tbb/task_group.h>
-#include <tbb/task_scheduler_init.h>
 #include <tbb/task_scheduler_observer.h>
+#if __has_include(<tbb/global_control.h>)
+
+#if TBB_INTERFACE_VERSION_MAJOR < 12
+#define TBB_PREVIEW_GLOBAL_CONTROL 1
+#endif
+
+#include <tbb/global_control.h>
+#else
+// deprecated in newer versions of TBB
+#include <tbb/task_scheduler_init.h>
+#endif
 
 namespace celix {
 
@@ -96,7 +106,7 @@ namespace celix {
          *
          * @return The Promise associated with this Deferred.
          */
-        Promise<T> getPromise();
+        [[nodiscard]] Promise<T> getPromise();
 
         /**
          * Successfully resolve the Promise associated with this Deferred.
@@ -188,7 +198,7 @@ namespace celix {
          *
          * @return The Promise associated with this Deferred.
          */
-        Promise<void> getPromise();
+        [[nodiscard]] Promise<void> getPromise();
 
         /**
          * Successfully resolve the Promise associated with this Deferred.
@@ -257,11 +267,9 @@ inline celix::Promise<void> celix::Deferred<void>::getPromise() {
 template<typename T>
 template<typename U>
 inline void celix::Deferred<T>::resolveWith(celix::Promise<U> with) {
-    auto s = state;
-    with.onResolve([s, with]{
+    with.onResolve([s = state, with] () mutable {
         if (with.isSuccessfullyResolved()) {
-            U val = with.getValue();
-            s->resolve(std::forward<T>(val));
+            s->resolve(with.moveOrGetValue());
         } else {
             s->fail(with.getFailure());
         }
@@ -269,8 +277,7 @@ inline void celix::Deferred<T>::resolveWith(celix::Promise<U> with) {
 }
 
 inline void celix::Deferred<void>::resolveWith(celix::Promise<void> with) {
-    auto s = state;
-    with.onResolve([s, with]{
+    with.onResolve([s = state, with]{
         if (with.isSuccessfullyResolved()) {
             with.getValue();
             s->resolve();
diff --git a/misc/experimental/promise/api/celix/Promise.h b/misc/experimental/promise/api/celix/Promise.h
index 8b58f26..dd8a501 100644
--- a/misc/experimental/promise/api/celix/Promise.h
+++ b/misc/experimental/promise/api/celix/Promise.h
@@ -71,7 +71,7 @@ namespace celix {
          * @return {@code true} if this Promise was resolved either successfully or
          *         with a failure; {@code false} if this Promise is unresolved.
          */
-        bool isDone() const;
+        [[nodiscard]] bool isDone() const;
 
         //
         /**
@@ -82,7 +82,7 @@ namespace celix {
          * @return {@code true} if this Promise was resolved successfully.
          *         {@code false} if this Promise is unresolved or resolved with a failure.
          */
-        bool isSuccessfullyResolved() const;
+        [[nodiscard]] bool isSuccessfullyResolved() const;
 
         /**
          * Returns the failure of this Promise.
@@ -101,7 +101,7 @@ namespace celix {
          * @throws InterruptedException If the current thread was interrupted while
          *         waiting.
          */
-        std::exception_ptr getFailure() const;
+        [[nodiscard]] std::exception_ptr getFailure() const;
 
         /**
          * Returns the value of this Promise.
@@ -123,10 +123,10 @@ namespace celix {
          * @throws InterruptedException If the current thread was interrupted while
          *         waiting.
          */
+        T& getValue();
         const T& getValue() const;
 
-        //TODO is a move value needed? Howto handle this with resolve callbacks
-        T moveValue();
+        [[nodiscard]] T moveOrGetValue();
 
         /**
          * Wait till the promise is resolved.
@@ -226,7 +226,7 @@ namespace celix {
          *                 value to be used to resolve the returned Promise. Must not be null.
          * @return A Promise that resolves with the value of this Promise or recovers from the failure of this Promise.
          */
-        Promise<T> recover(std::function<T()> recover);
+        [[nodiscard]] Promise<T> recover(std::function<T()> recover);
 
 
         /**
@@ -241,7 +241,7 @@ namespace celix {
          * @param the consumer callback
          * @returns A new Promise which is chained to this Promise. The returned Promise must be resolved when this Promise is resolved after the specified Consumer is executed.
          */
-        Promise<T> thenAccept(std::function<void(T)> consumer);
+        [[nodiscard]] Promise<T> thenAccept(std::function<void(T)> consumer);
 
         /**
          * Fall back to the value of the specified Promise if this Promise fails.
@@ -258,7 +258,7 @@ namespace celix {
          *                 with a failure. Must not be null.
          * @return A Promise that returns the value of this Promise or falls back to the value of the specified Promise.
          */
-        Promise<T> fallbackTo(celix::Promise<T> fallback);
+        [[nodiscard]] Promise<T> fallbackTo(celix::Promise<T> fallback);
 
         /**
          * Map the value of this Promise.
@@ -277,7 +277,7 @@ namespace celix {
          * @return A Promise that returns the value of this Promise as mapped by the specified Function.
          */
         template<typename R>
-        celix::Promise<R> map(std::function<R(T)> mapper);
+        [[nodiscard]] celix::Promise<R> map(std::function<R(T)> mapper);
 
         /**
          * Filter the value of this Promise.
@@ -294,7 +294,7 @@ namespace celix {
          * @param predicate The Predicate to evaluate the value of this Promise.
          * @return A Promise that filters the value of this Promise.
          */
-        Promise<T> filter(std::function<bool(T)> predicate);
+        [[nodiscard]] Promise<T> filter(std::function<bool(T)> predicate);
 
         /**
          * Time out the resolution of this Promise.
@@ -312,7 +312,7 @@ namespace celix {
          *         or the specified timeout is reached.
          */
         template<typename Rep, typename Period>
-        Promise<T> timeout(std::chrono::duration<Rep, Period> duration);
+        [[nodiscard]] Promise<T> timeout(std::chrono::duration<Rep, Period> duration);
 
         /**
          * Delay after the resolution of this Promise.
@@ -326,7 +326,7 @@ namespace celix {
          *         is resolved and the specified delay has elapsed.
          */
         template<typename Rep, typename Period>
-        Promise<T> delay(std::chrono::duration<Rep, Period> duration);
+        [[nodiscard]] Promise<T> delay(std::chrono::duration<Rep, Period> duration);
 
         /**
          * FlatMap the value of this Promise.
@@ -346,7 +346,7 @@ namespace celix {
          */
 //        TODO
 //        template<typename R>
-//        celix::Promise<R> flatMap(std::function<celix::Promise<R>(T)> mapper);
+//        [[nodiscard]] celix::Promise<R> flatMap(std::function<celix::Promise<R>(T)> mapper);
 
         /**
          * Chain a new Promise to this Promise with success and failure callbacks.
@@ -381,7 +381,21 @@ namespace celix {
          * is resolved after the specified Success or Failure callback, if any, is executed
          */
         template<typename U>
-        celix::Promise<U> then(std::function<celix::Promise<U>(celix::Promise<T>)> success, std::function<void(celix::Promise<T>)> failure = {});
+        [[nodiscard]] celix::Promise<U> then(std::function<celix::Promise<U>(celix::Promise<T>)> success, std::function<void(celix::Promise<T>)> failure = {});
+
+        /**
+         * Convenience operator calling getValue()
+         */
+        constexpr const T&
+        operator*() const
+        { return this->getValue(); }
+
+        /**
+         * Convenience operator calling getValue()
+         */
+        constexpr T&
+        operator*()
+        { return this->getValue(); }
     private:
         const std::shared_ptr<celix::impl::SharedPromiseState<T>> state;
 
@@ -395,11 +409,11 @@ namespace celix {
 
         explicit Promise(std::shared_ptr<celix::impl::SharedPromiseState<void>> s);
 
-        bool isDone() const;
+        [[nodiscard]] bool isDone() const;
 
-        bool isSuccessfullyResolved() const;
+        [[nodiscard]] bool isSuccessfullyResolved() const;
 
-        std::exception_ptr getFailure() const;
+        [[nodiscard]] std::exception_ptr getFailure() const;
 
         bool getValue() const; // NOLINT(modernize-use-nodiscard)
 
@@ -411,23 +425,23 @@ namespace celix {
 
         Promise<void>& onResolve(std::function<void()> callback);
 
-        Promise<void> recover(std::function<void()> recover);
+        [[nodiscard]] Promise<void> recover(std::function<void()> recover);
 
-        Promise<void> thenAccept(std::function<void()> consumer);
+        [[nodiscard]] Promise<void> thenAccept(std::function<void()> consumer);
 
-        Promise<void> fallbackTo(celix::Promise<void> fallback);
+        [[nodiscard]] Promise<void> fallbackTo(celix::Promise<void> fallback);
 
         template<typename R>
-        celix::Promise<R> map(std::function<R()> mapper);
+        [[nodiscard]] celix::Promise<R> map(std::function<R()> mapper);
 
         template<typename Rep, typename Period>
-        Promise<void> timeout(std::chrono::duration<Rep, Period> duration);
+        [[nodiscard]] Promise<void> timeout(std::chrono::duration<Rep, Period> duration);
 
         template<typename Rep, typename Period>
-        Promise<void> delay(std::chrono::duration<Rep, Period> duration);
+        [[nodiscard]] Promise<void> delay(std::chrono::duration<Rep, Period> duration);
 
         template<typename U>
-        celix::Promise<U> then(std::function<celix::Promise<U>(celix::Promise<void>)> success, std::function<void(celix::Promise<void>)> failure = {});
+        [[nodiscard]] celix::Promise<U> then(std::function<celix::Promise<U>(celix::Promise<void>)> success, std::function<void(celix::Promise<void>)> failure = {});
     private:
         const std::shared_ptr<celix::impl::SharedPromiseState<void>> state;
     };
@@ -446,6 +460,11 @@ inline celix::Promise<void>::Promise(std::shared_ptr<celix::impl::SharedPromiseS
 }
 
 template<typename T>
+inline T& celix::Promise<T>::getValue() {
+    return state->getValue();
+}
+
+template<typename T>
 inline const T& celix::Promise<T>::getValue() const {
     return state->getValue();
 }
@@ -455,8 +474,8 @@ inline bool celix::Promise<void>::getValue() const {
 }
 
 template<typename T>
-inline T celix::Promise<T>::moveValue() {
-    return state->moveValue();
+inline T celix::Promise<T>::moveOrGetValue() {
+    return state->moveOrGetValue();
 }
 
 template<typename T>
@@ -597,10 +616,9 @@ inline void celix::Promise<void>::wait() const {
 template<typename T>
 template<typename U>
 inline celix::Promise<U> celix::Promise<T>::then(std::function<celix::Promise<U>(celix::Promise<T>)> success, std::function<void(celix::Promise<T>)> failure) {
-    auto s = state;
     auto p = std::make_shared<celix::impl::SharedPromiseState<U>>(state->getExecutor());
 
-    auto chain = [s, p, success, failure]() {
+    auto chain = [s = state, p, success = std::move(success), failure = std::move(failure)]() {
         //chain is called when s is resolved
         if (s->isSuccessfullyResolved()) {
             try {
@@ -623,10 +641,9 @@ inline celix::Promise<U> celix::Promise<T>::then(std::function<celix::Promise<U>
 
 template<typename U>
 inline celix::Promise<U> celix::Promise<void>::then(std::function<celix::Promise<U>(celix::Promise<void>)> success, std::function<void(celix::Promise<void>)> failure) {
-    auto s = state;
     auto p = std::make_shared<celix::impl::SharedPromiseState<U>>(state->getExecutor());
 
-    auto chain = [s, p, success, failure]() {
+    auto chain = [s = state, p, success = std::move(success), failure = std::move(failure)]() {
         //chain is called when s is resolved
         if (s->isSuccessfullyResolved()) {
             try {
diff --git a/misc/experimental/promise/api/celix/PromiseFactory.h b/misc/experimental/promise/api/celix/PromiseFactory.h
index a9b81a0..23a23b2 100644
--- a/misc/experimental/promise/api/celix/PromiseFactory.h
+++ b/misc/experimental/promise/api/celix/PromiseFactory.h
@@ -30,18 +30,18 @@ namespace celix {
         //TODO ctor with callbackExecutor and scheduledExecutor
 
         template<typename T>
-        celix::Deferred<T> deferred();
+        [[nodiscard]] celix::Deferred<T> deferred();
 
         template<typename T>
-        celix::Promise<T> failed(const std::exception& e);
+        [[nodiscard]] celix::Promise<T> failed(const std::exception& e);
 
         template<typename T>
-        celix::Promise<T> failed(std::exception_ptr ptr);
+        [[nodiscard]] celix::Promise<T> failed(std::exception_ptr ptr);
 
         template<typename T>
-        celix::Promise<T> resolved(T&& value);
+        [[nodiscard]] celix::Promise<T> resolved(T&& value);
 
-        celix::Promise<void> resolved();
+        [[nodiscard]] celix::Promise<void> resolved();
 
         //TODO rest
     private:
diff --git a/misc/experimental/promise/api/celix/PromiseInvocationException.h b/misc/experimental/promise/api/celix/PromiseInvocationException.h
index b529224..76aa350 100644
--- a/misc/experimental/promise/api/celix/PromiseInvocationException.h
+++ b/misc/experimental/promise/api/celix/PromiseInvocationException.h
@@ -20,6 +20,7 @@
 #pragma once
 
 #include <exception>
+#include <utility>
 
 
 namespace celix {
@@ -27,6 +28,7 @@ namespace celix {
     class PromiseInvocationException : public std::exception {
     public:
         explicit PromiseInvocationException(const char* what) : w{what} {}
+        explicit PromiseInvocationException(std::string what) : w{std::move(what)} {}
 
         PromiseInvocationException(const PromiseInvocationException&) = delete;
         PromiseInvocationException(PromiseInvocationException&&) noexcept = default;
@@ -34,9 +36,9 @@ namespace celix {
         PromiseInvocationException& operator=(const PromiseInvocationException&) = delete;
         PromiseInvocationException& operator=(PromiseInvocationException&&) noexcept = default;
 
-        const char* what() const noexcept override { return w; }
+        const char* what() const noexcept override { return w.c_str(); }
     private:
-        const char* w;
+        std::string w;
     };
 }
 
diff --git a/misc/experimental/promise/api/celix/impl/SharedPromiseState.h b/misc/experimental/promise/api/celix/impl/SharedPromiseState.h
index 7c6ab1c..6c1dd70 100644
--- a/misc/experimental/promise/api/celix/impl/SharedPromiseState.h
+++ b/misc/experimental/promise/api/celix/impl/SharedPromiseState.h
@@ -26,175 +26,180 @@
 #include <utility>
 #include <vector>
 #include <thread>
+#include <optional>
 
 #include <tbb/task_arena.h>
 
 #include "celix/PromiseInvocationException.h"
 #include "celix/PromiseTimeoutException.h"
 
-namespace celix {
-    namespace impl {
+namespace celix::impl {
 
-        template<typename T>
-        class SharedPromiseState {
-        public:
-            typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type DataType;
+    template<typename T>
+    class SharedPromiseState {
+        // Pointers make using promises properly unnecessarily complicated.
+        static_assert(!std::is_pointer_v<T>, "Cannot use pointers with promises.");
+    public:
+        explicit SharedPromiseState(const tbb::task_arena &executor = {});
 
-            explicit SharedPromiseState(const tbb::task_arena &executor = {});
+        ~SharedPromiseState() = default;
 
-            ~SharedPromiseState();
+        void resolve(T&& value);
 
-            void resolve(T &&value);
+        void resolve(const T& value);
 
-            void resolve(const T& value);
+        void fail(std::exception_ptr e);
 
-            void fail(std::exception_ptr p);
+        void fail(const std::exception &e);
 
-            void fail(const std::exception &e);
+        void tryResolve(T &&value);
 
-            void tryResolve(T &&value);
+        void tryFail(std::exception_ptr e);
 
-            void tryFail(std::exception_ptr p);
+        // copy/move depending on situation
+        T& getValue() &;
+        const T& getValue() const &;
+        [[nodiscard]] T&& getValue() &&;
+        [[nodiscard]] const T&& getValue() const &&;
 
-            const T &getValue() const; //copy
-            T moveValue(); //move
-            std::exception_ptr getFailure() const;
+        // move if T is moveable
+        [[nodiscard]] T moveOrGetValue();
 
-            void wait() const;
+        [[nodiscard]] std::exception_ptr getFailure() const;
 
-            bool isDone() const;
+        void wait() const;
 
-            bool isSuccessfullyResolved() const;
+        [[nodiscard]] bool isDone() const;
 
-            void addOnSuccessConsumeCallback(std::function<void(T)> callback);
+        [[nodiscard]] bool isSuccessfullyResolved() const;
 
-            void addOnFailureConsumeCallback(std::function<void(const std::exception &)> callback);
+        void addOnSuccessConsumeCallback(std::function<void(T)> callback);
 
-            void addOnResolve(std::function<void(bool succeeded, T *val, std::exception_ptr exp)> callback);
+        void addOnFailureConsumeCallback(std::function<void(const std::exception &)> callback);
 
-            template<typename Rep, typename Period>
-            std::shared_ptr<SharedPromiseState<T>> delay(std::chrono::duration<Rep, Period> duration);
+        void addOnResolve(std::function<void(std::optional<T> val, std::exception_ptr exp)> callback);
 
-            std::shared_ptr<SharedPromiseState<T>> recover(std::function<T()> recover);
+        template<typename Rep, typename Period>
+        [[nodiscard]] std::shared_ptr<SharedPromiseState<T>> delay(std::chrono::duration<Rep, Period> duration);
 
-            std::shared_ptr<SharedPromiseState<T>> filter(std::function<bool(T)> predicate);
+        [[nodiscard]] std::shared_ptr<SharedPromiseState<T>> recover(std::function<T()> recover);
 
-            std::shared_ptr<SharedPromiseState<T>> fallbackTo(std::shared_ptr<SharedPromiseState<T>> fallbackTo);
+        [[nodiscard]] std::shared_ptr<SharedPromiseState<T>> filter(std::function<bool(const T&)> predicate);
 
-            void resolveWith(std::shared_ptr<SharedPromiseState<T>> with);
+        [[nodiscard]] std::shared_ptr<SharedPromiseState<T>> fallbackTo(std::shared_ptr<SharedPromiseState<T>> fallbackTo);
 
-            template<typename R>
-            std::shared_ptr<SharedPromiseState<R>> map(std::function<R(T)> mapper);
+        void resolveWith(std::shared_ptr<SharedPromiseState<T>> with);
 
-            std::shared_ptr<SharedPromiseState<T>> thenAccept(std::function<void(T)> consumer);
+        template<typename R>
+        [[nodiscard]] std::shared_ptr<SharedPromiseState<R>> map(std::function<R(T)> mapper);
 
-            template<typename Rep, typename Period>
-            static std::shared_ptr<SharedPromiseState<T>>
-            timeout(std::shared_ptr<SharedPromiseState<T>> state, std::chrono::duration<Rep, Period> duration);
+        [[nodiscard]] std::shared_ptr<SharedPromiseState<T>> thenAccept(std::function<void(T)> consumer);
 
-            void addChain(std::function<void()> chainFunction);
+        template<typename Rep, typename Period>
+        [[nodiscard]] static std::shared_ptr<SharedPromiseState<T>>
+        timeout(std::shared_ptr<SharedPromiseState<T>> state, std::chrono::duration<Rep, Period> duration);
 
-            tbb::task_arena getExecutor() const;
-        private:
-            /**
-             * Complete the resolving and call the registered tasks
-             * A reference to the possible locked unique_lock.
-             */
-            void complete(std::unique_lock<std::mutex> &lck);
+        void addChain(std::function<void()> chainFunction);
 
-            /**
-             * Wait for data and check if it resolved as expected (expects mutex locked)
-             */
-            void waitForAndCheckData(std::unique_lock<std::mutex> &lck, bool expectValid) const;
+        tbb::task_arena getExecutor() const;
+    private:
+        /**
+         * Complete the resolving and call the registered tasks
+         * A reference to the possible locked unique_lock.
+         */
+        void complete(std::unique_lock<std::mutex> &lck);
 
-            tbb::task_arena executor; //TODO look into different thread pool libraries
-            //TODO add ScheduledExecutorService like object
+        /**
+         * Wait for data and check if it resolved as expected (expects mutex locked)
+         */
+        void waitForAndCheckData(std::unique_lock<std::mutex> &lck, bool expectValid) const;
 
-            mutable std::mutex mutex{}; //protects below
-            mutable std::condition_variable cond{};
-            bool done = false;
-            bool dataMoved = false;
-            std::vector<std::function<void()>> chain{}; //chain tasks are executed on thread pool.
-            std::exception_ptr exp{nullptr};
-            DataType data{};
-        };
+        tbb::task_arena executor; //TODO look into different thread pool libraries
+        //TODO add ScheduledExecutorService like object
 
-        template<>
-        class SharedPromiseState<void> {
-        public:
-            explicit SharedPromiseState(const tbb::task_arena &executor = {});
+        mutable std::mutex mutex{}; //protects below
+        mutable std::condition_variable cond{};
+        bool done = false;
+        bool dataMoved = false;
+        std::vector<std::function<void()>> chain{}; //chain tasks are executed on thread pool.
+        std::exception_ptr exp{nullptr};
+        std::optional<T> data{};
+    };
 
-            ~SharedPromiseState() = default;
+    template<>
+    class SharedPromiseState<void> {
+    public:
+        explicit SharedPromiseState(const tbb::task_arena &executor = {});
 
-            void resolve();
+        ~SharedPromiseState() = default;
 
-            void fail(std::exception_ptr p);
+        void resolve();
 
-            void fail(const std::exception &e);
+        void fail(std::exception_ptr e);
 
-            void tryResolve();
+        void fail(const std::exception &e);
 
-            void tryFail(std::exception_ptr p);
+        void tryResolve();
 
-            bool getValue() const; //copy
-            std::exception_ptr getFailure() const;
+        void tryFail(std::exception_ptr e);
 
-            void wait() const;
+        bool getValue() const;
+        std::exception_ptr getFailure() const;
 
-            bool isDone() const;
+        void wait() const;
 
-            bool isSuccessfullyResolved() const;
+        bool isDone() const;
 
-            void addOnSuccessConsumeCallback(std::function<void()> callback);
+        bool isSuccessfullyResolved() const;
 
-            void addOnFailureConsumeCallback(std::function<void(const std::exception &)> callback);
+        void addOnSuccessConsumeCallback(std::function<void()> callback);
 
-            void addOnResolve(std::function<void(bool succeeded, std::exception_ptr exp)> callback);
+        void addOnFailureConsumeCallback(std::function<void(const std::exception &)> callback);
 
-            template<typename Rep, typename Period>
-            std::shared_ptr<SharedPromiseState<void>> delay(std::chrono::duration<Rep, Period> duration);
+        void addOnResolve(std::function<void(std::optional<std::exception_ptr> exp)> callback);
 
-            std::shared_ptr<SharedPromiseState<void>> recover(std::function<void()> recover);
+        template<typename Rep, typename Period>
+        std::shared_ptr<SharedPromiseState<void>> delay(std::chrono::duration<Rep, Period> duration);
 
-            std::shared_ptr<SharedPromiseState<void>> fallbackTo(std::shared_ptr<SharedPromiseState<void>> fallbackTo);
+        std::shared_ptr<SharedPromiseState<void>> recover(std::function<void()> recover);
 
-            void resolveWith(std::shared_ptr<SharedPromiseState<void>> with);
+        std::shared_ptr<SharedPromiseState<void>> fallbackTo(std::shared_ptr<SharedPromiseState<void>> fallbackTo);
 
-            template<typename R>
-            std::shared_ptr<SharedPromiseState<R>> map(std::function<R(void)> mapper);
+        void resolveWith(std::shared_ptr<SharedPromiseState<void>> with);
 
-            std::shared_ptr<SharedPromiseState<void>> thenAccept(std::function<void()> consumer);
+        template<typename R>
+        std::shared_ptr<SharedPromiseState<R>> map(std::function<R(void)> mapper);
 
-            template<typename Rep, typename Period>
-            static std::shared_ptr<SharedPromiseState<void>>
-            timeout(std::shared_ptr<SharedPromiseState<void>> state, std::chrono::duration<Rep, Period> duration);
+        std::shared_ptr<SharedPromiseState<void>> thenAccept(std::function<void()> consumer);
 
-            void addChain(std::function<void()> chainFunction);
+        template<typename Rep, typename Period>
+        static std::shared_ptr<SharedPromiseState<void>>
+        timeout(std::shared_ptr<SharedPromiseState<void>> state, std::chrono::duration<Rep, Period> duration);
 
-            tbb::task_arena getExecutor() const;
-        private:
-            /**
-             * Complete the resolving and call the registered tasks
-             * A reference to the possible locked unique_lock.
-             */
-            void complete(std::unique_lock<std::mutex> &lck);
+        void addChain(std::function<void()> chainFunction);
 
-            /**
-             * Wait for data and check if it resolved as expected (expects mutex locked)
-             */
-            void waitForAndCheckData(std::unique_lock<std::mutex> &lck, bool expectValid) const;
+        tbb::task_arena getExecutor() const;
+    private:
+        /**
+         * Complete the resolving and call the registered tasks
+         * A reference to the possible locked unique_lock.
+         */
+        void complete(std::unique_lock<std::mutex> &lck);
 
-            tbb::task_arena executor; //TODO look into different thread pool libraries
-            //TODO add ScheduledExecutorService like object
+        /**
+         * Wait for data and check if it resolved as expected (expects mutex locked)
+         */
+        void waitForAndCheckData(std::unique_lock<std::mutex> &lck, bool expectValid) const;
 
-            mutable std::mutex mutex{}; //protects below
-            mutable std::condition_variable cond{};
-            bool done = false;
-            bool dataMoved = false;
-            std::vector<std::function<void()>> chain{}; //chain tasks are executed on thread pool.
-            std::exception_ptr exp{nullptr};
-        };
-    }
+        tbb::task_arena executor; //TODO look into different thread pool libraries
+        //TODO add ScheduledExecutorService like object
+
+        mutable std::mutex mutex{}; //protects below
+        mutable std::condition_variable cond{};
+        bool done = false;
+        std::vector<std::function<void()>> chain{}; //chain tasks are executed on thread pool.
+        std::exception_ptr exp{nullptr};
+    };
 }
 
 
@@ -208,25 +213,17 @@ inline celix::impl::SharedPromiseState<T>::SharedPromiseState(const tbb::task_ar
 inline celix::impl::SharedPromiseState<void>::SharedPromiseState(const tbb::task_arena& _executor) : executor{_executor} {}
 
 template<typename T>
-inline celix::impl::SharedPromiseState<T>::~SharedPromiseState() {
-    std::unique_lock<std::mutex> lck{mutex};
-
-    //Note for now, not waiting until promise is met.
-    //Else if a deferred goes out of scope without resolving, a wait will block
-    //cond.wait(lck, [this]{return done;});
-
-    if (done && !exp && !dataMoved) {
-        static_cast<T*>(static_cast<void*>(&data))->~T();
-    }
-}
-
-template<typename T>
 inline void celix::impl::SharedPromiseState<T>::resolve(T&& value) {
     std::unique_lock<std::mutex> lck{mutex};
     if (done) {
         throw celix::PromiseInvocationException("Cannot resolve Promise. Promise is already done");
     }
-    new(&data) T{std::forward<T>(value)};
+    dataMoved = false;
+    if constexpr (std::is_move_constructible_v<T>) {
+        data = std::forward<T>(value);
+    } else {
+        data = value;
+    }
     exp = nullptr;
     complete(lck);
 }
@@ -238,7 +235,8 @@ inline void celix::impl::SharedPromiseState<T>::resolve(const T& value) {
     if (done) {
         throw celix::PromiseInvocationException("Cannot resolve Promise. Promise is already done");
     }
-    new(&data) T{value};
+    dataMoved = false;
+    data = value;
     exp = nullptr;
     complete(lck);
 }
@@ -284,7 +282,8 @@ template<typename T>
 inline void celix::impl::SharedPromiseState<T>::tryResolve(T&& value) {
     std::unique_lock<std::mutex> lck{mutex};
     if (!done) {
-        new(&data) T(std::forward<T>(value));
+        dataMoved = false;
+        data = std::forward<T>(value);
         exp = nullptr;
         complete(lck);
     }
@@ -345,7 +344,15 @@ inline void celix::impl::SharedPromiseState<T>::waitForAndCheckData(std::unique_
     }
     cond.wait(lck, [this]{return done;});
     if (expectValid && exp) {
-        throw celix::PromiseInvocationException{"Expected a succeeded promise, but promise failed"};
+        std::string what;
+        try {
+            std::rethrow_exception(exp);
+        } catch (const std::exception &e) {
+            what = e.what();
+        } catch (...) {
+            what = "unknown exception";
+        }
+        throw celix::PromiseInvocationException{"Expected a succeeded promise, but promise failed with message \"" + what + "\""};
     } else if(!expectValid && !exp && !dataMoved) {
         throw celix::PromiseInvocationException{"Expected a failed promise, but promise succeeded"};
     } else if (dataMoved) {
@@ -359,20 +366,46 @@ inline void celix::impl::SharedPromiseState<void>::waitForAndCheckData(std::uniq
     }
     cond.wait(lck, [this]{return done;});
     if (expectValid && exp) {
-        throw celix::PromiseInvocationException{"Expected a succeeded promise, but promise failed"};
-    } else if(!expectValid && !exp && !dataMoved) {
+        std::string what;
+        try {
+            std::rethrow_exception(exp);
+        } catch (const std::exception &e) {
+            what = e.what();
+        } catch (...) {
+            what = "unknown exception";
+        }
+        throw celix::PromiseInvocationException{"Expected a succeeded promise, but promise failed with message \"" + what + "\""};
+    } else if(!expectValid && !exp) {
         throw celix::PromiseInvocationException{"Expected a failed promise, but promise succeeded"};
-    } else if (dataMoved) {
-        throw celix::PromiseInvocationException{"Invalid use of promise, data is moved and not available anymore!"};
     }
 }
 
 template<typename T>
-inline const T& celix::impl::SharedPromiseState<T>::getValue() const {
+inline T& celix::impl::SharedPromiseState<T>::getValue() & {
+    std::unique_lock<std::mutex> lck{mutex};
+    waitForAndCheckData(lck, true);
+    return *data;
+}
+
+template<typename T>
+inline const T& celix::impl::SharedPromiseState<T>::getValue() const & {
     std::unique_lock<std::mutex> lck{mutex};
     waitForAndCheckData(lck, true);
-    const T* ptr = reinterpret_cast<const T*>(&data);
-    return *ptr;
+    return *data;
+}
+
+template<typename T>
+inline T&& celix::impl::SharedPromiseState<T>::getValue() && {
+    std::unique_lock<std::mutex> lck{mutex};
+    waitForAndCheckData(lck, true);
+    return std::move(*data);
+}
+
+template<typename T>
+inline const T&& celix::impl::SharedPromiseState<T>::getValue() const && {
+    std::unique_lock<std::mutex> lck{mutex};
+    waitForAndCheckData(lck, true);
+    return std::move(*data);
 }
 
 inline bool celix::impl::SharedPromiseState<void>::getValue() const {
@@ -382,6 +415,18 @@ inline bool celix::impl::SharedPromiseState<void>::getValue() const {
 }
 
 template<typename T>
+inline T celix::impl::SharedPromiseState<T>::moveOrGetValue() {
+    std::unique_lock<std::mutex> lck{mutex};
+    waitForAndCheckData(lck, true);
+    if constexpr (std::is_move_constructible_v<T>) {
+        dataMoved = true;
+        return std::move(*data);
+    } else {
+        return *data;
+    }
+}
+
+template<typename T>
 inline tbb::task_arena celix::impl::SharedPromiseState<T>::getExecutor() const {
     return executor;
 }
@@ -391,15 +436,6 @@ inline tbb::task_arena celix::impl::SharedPromiseState<void>::getExecutor() cons
 }
 
 template<typename T>
-inline T celix::impl::SharedPromiseState<T>::moveValue() {
-    std::unique_lock<std::mutex> lck{mutex};
-    waitForAndCheckData(lck, true);
-    dataMoved = true;
-    T* ptr = reinterpret_cast<T*>(&data);
-    return T{std::move(*ptr)};
-};
-
-template<typename T>
 inline void celix::impl::SharedPromiseState<T>::wait() const {
     std::unique_lock<std::mutex> lck{mutex};
     cond.wait(lck, [this]{return done;});
@@ -425,9 +461,9 @@ inline std::exception_ptr celix::impl::SharedPromiseState<void>::getFailure() co
 
 template<typename T>
 inline void celix::impl::SharedPromiseState<T>::resolveWith(std::shared_ptr<SharedPromiseState<T>> with) {
-    with->addOnResolve([this](bool succeeded, T* v, std::exception_ptr e) {
-        if (succeeded) {
-            tryResolve(std::forward<T>(*v));
+    with->addOnResolve([this](std::optional<T> v, std::exception_ptr e) {
+        if (v) {
+            tryResolve(std::move(*v));
         } else {
             tryFail(std::move(e));
         }
@@ -435,11 +471,11 @@ inline void celix::impl::SharedPromiseState<T>::resolveWith(std::shared_ptr<Shar
 }
 
 inline void celix::impl::SharedPromiseState<void>::resolveWith(std::shared_ptr<SharedPromiseState<void>> with) {
-    with->addOnResolve([this](bool succeeded, std::exception_ptr e) {
-        if (succeeded) {
+    with->addOnResolve([this](std::optional<std::exception_ptr> e) {
+        if (!e) {
             tryResolve();
         } else {
-            tryFail(std::move(e));
+            tryFail(std::move(*e));
         }
     });
 }
@@ -474,11 +510,11 @@ template<typename Rep, typename Period>
 inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPromiseState<T>::delay(std::chrono::duration<Rep, Period> duration) {
     auto p = std::make_shared<celix::impl::SharedPromiseState<T>>(executor);
 
-    addOnResolve([p, duration](bool succeeded, T* v, std::exception_ptr e) {
+    addOnResolve([p, duration](std::optional<T> v, std::exception_ptr e) {
         std::this_thread::sleep_for(duration); //TODO use scheduler instead of sleep on thread (using unnecessary resources)
         try {
-            if (succeeded) {
-                p->resolve(std::forward<T>(*v));
+            if (v) {
+                p->resolve(std::move(*v));
             } else {
                 p->fail(std::move(e));
             }
@@ -496,13 +532,13 @@ template<typename Rep, typename Period>
 inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::SharedPromiseState<void>::delay(std::chrono::duration<Rep, Period> duration) {
     auto p = std::make_shared<celix::impl::SharedPromiseState<void>>(executor);
 
-    addOnResolve([p, duration](bool succeeded, std::exception_ptr e) {
+    addOnResolve([p, duration](std::optional<std::exception_ptr> e) {
         std::this_thread::sleep_for(duration); //TODO use scheduler instead of sleep on thread (using unnecessary resources)
         try {
-            if (succeeded) {
+            if (!e) {
                 p->resolve();
             } else {
-                p->fail(std::move(e));
+                p->fail(std::move(*e));
             }
         } catch (celix::PromiseInvocationException&) {
             //somebody already resolved p?
@@ -521,9 +557,9 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPr
     }
     auto p = std::make_shared<celix::impl::SharedPromiseState<T>>(executor);
 
-    addOnResolve([p, recover](bool succeeded, T *v, const std::exception_ptr& /*e*/) {
-        if (succeeded) {
-            p->resolve(std::forward<T>(*v));
+    addOnResolve([p, recover = std::move(recover)](std::optional<T> v, const std::exception_ptr& /*e*/) {
+        if (v) {
+            p->resolve(std::move(*v));
         }  else {
             try {
                 p->resolve(recover());
@@ -541,8 +577,8 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::Share
     }
     auto p = std::make_shared<celix::impl::SharedPromiseState<void>>(executor);
 
-    addOnResolve([p, recover](bool succeeded, const std::exception_ptr& /*e*/) {
-        if (succeeded) {
+    addOnResolve([p, recover = std::move(recover)](std::optional<std::exception_ptr> e) {
+        if (!e) {
             p->resolve();
         }  else {
             try {
@@ -557,17 +593,16 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::Share
 }
 
 template<typename T>
-inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPromiseState<T>::filter(std::function<bool(T)> predicate) {
+inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPromiseState<T>::filter(std::function<bool(const T&)> predicate) {
     if (!predicate) {
         throw celix::PromiseInvocationException{"provided predicate callback is not valid"};
     }
     auto p = std::make_shared<celix::impl::SharedPromiseState<T>>(executor);
-    auto chainFunction = [this, p, predicate] {
+    auto chainFunction = [this, p, predicate = std::move(predicate)] {
         if (isSuccessfullyResolved()) {
-            T val = getValue();
             try {
-                if (predicate(std::forward<T>(val))) {
-                    p->resolve(std::forward<T>(val));
+                if (predicate(getValue())) {
+                    p->resolve(moveOrGetValue());
                 } else {
                     throw celix::PromiseInvocationException{"predicate does not accept value"};
                 }
@@ -586,14 +621,12 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPr
 template<typename T>
 inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPromiseState<T>::fallbackTo(std::shared_ptr<celix::impl::SharedPromiseState<T>> fallbackTo) {
     auto p = std::make_shared<celix::impl::SharedPromiseState<T>>(executor);
-    auto chainFunction = [this, p, fallbackTo] {
+    auto chainFunction = [this, p, fallbackTo = std::move(fallbackTo)] {
         if (isSuccessfullyResolved()) {
-            T val = getValue();
-            p->resolve(std::forward<T>(val));
+            p->resolve(moveOrGetValue());
         } else {
             if (fallbackTo->isSuccessfullyResolved()) {
-                T val = fallbackTo->getValue();
-                p->resolve(std::forward<T>(val));
+                p->resolve(fallbackTo->moveOrGetValue());
             } else {
                 p->fail(getFailure());
             }
@@ -605,7 +638,7 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPr
 
 inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::SharedPromiseState<void>::fallbackTo(std::shared_ptr<celix::impl::SharedPromiseState<void>> fallbackTo) {
     auto p = std::make_shared<celix::impl::SharedPromiseState<void>>(executor);
-    auto chainFunction = [this, p, fallbackTo] {
+    auto chainFunction = [this, p, fallbackTo = std::move(fallbackTo)] {
         if (isSuccessfullyResolved()) {
             getValue();
             p->resolve();
@@ -626,7 +659,6 @@ template<typename T>
 inline void celix::impl::SharedPromiseState<T>::addChain(std::function<void()> chainFunction) {
     std::function<void()> localChain{};
     {
-
         std::lock_guard<std::mutex> lck{mutex};
         if (!done) {
             chain.push_back(std::move(chainFunction));
@@ -642,7 +674,6 @@ inline void celix::impl::SharedPromiseState<T>::addChain(std::function<void()> c
 inline void celix::impl::SharedPromiseState<void>::addChain(std::function<void()> chainFunction) {
     std::function<void()> localChain{};
     {
-
         std::lock_guard<std::mutex> lck{mutex};
         if (!done) {
             chain.push_back(std::move(chainFunction));
@@ -662,11 +693,10 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<R>> celix::impl::SharedPr
         throw celix::PromiseInvocationException("provided mapper is not valid");
     }
     auto p = std::make_shared<celix::impl::SharedPromiseState<R>>(executor);
-    auto chainFunction = [this, p, mapper] {
+    auto chainFunction = [this, p, mapper = std::move(mapper)] {
         try {
             if (isSuccessfullyResolved()) {
-                R val = mapper(getValue());
-                p->resolve(std::forward<R>(val));
+                p->resolve(mapper(moveOrGetValue()));
             } else {
                 p->fail(getFailure());
             }
@@ -684,12 +714,11 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<R>> celix::impl::SharedPr
         throw celix::PromiseInvocationException("provided mapper is not valid");
     }
     auto p = std::make_shared<celix::impl::SharedPromiseState<R>>(executor);
-    auto chainFunction = [this, p, mapper] {
+    auto chainFunction = [this, p, mapper = std::move(mapper)] {
         try {
             if (isSuccessfullyResolved()) {
                 getValue();
-                R val = mapper();
-                p->resolve(std::forward<R>(val));
+                p->resolve(mapper());
             } else {
                 p->fail(getFailure());
             }
@@ -707,12 +736,11 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<T>> celix::impl::SharedPr
         throw celix::PromiseInvocationException("provided consumer is not valid");
     }
     auto p = std::make_shared<celix::impl::SharedPromiseState<T>>(executor);
-    auto chainFunction = [this, p, consumer] {
+    auto chainFunction = [this, p, consumer = std::move(consumer)] {
         if (isSuccessfullyResolved()) {
             try {
-                T val = getValue();
-                consumer(std::forward<T>(val));
-                p->resolve(std::forward<T>(val));
+                consumer(getValue());
+                p->resolve(moveOrGetValue());
             } catch (...) {
                 p->fail(std::current_exception());
             }
@@ -729,7 +757,7 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::Share
         throw celix::PromiseInvocationException("provided consumer is not valid");
     }
     auto p = std::make_shared<celix::impl::SharedPromiseState<void>>(executor);
-    auto chainFunction = [this, p, consumer] {
+    auto chainFunction = [this, p, consumer = std::move(consumer)] {
         if (isSuccessfullyResolved()) {
             try {
                 getValue();
@@ -747,35 +775,37 @@ inline std::shared_ptr<celix::impl::SharedPromiseState<void>> celix::impl::Share
 }
 
 template<typename T>
-inline void celix::impl::SharedPromiseState<T>::addOnResolve(std::function<void(bool succeeded, T* val, std::exception_ptr exp)> callback) {
-    std::function<void()> task = [this, callback] {
+inline void celix::impl::SharedPromiseState<T>::addOnResolve(std::function<void(std::optional<T> val, std::exception_ptr exp)> callback) {
+    std::function<void()> task = [this, callback = std::move(callback)] {
         std::exception_ptr e = nullptr;
-        T* val = nullptr;
         {
             std::lock_guard<std::mutex> lck{mutex};
             e = exp;
-            val = e ? nullptr : reinterpret_cast<T*>(&data);
         }
-        callback(!e, val, e);
+        if(e) {
+            callback({}, e);
+        } else {
+            callback(getValue(), e);
+        }
     };
     addChain(task);
 }
 
-inline void celix::impl::SharedPromiseState<void>::addOnResolve(std::function<void(bool succeeded, std::exception_ptr exp)> callback) {
-    std::function<void()> task = [this, callback] {
+inline void celix::impl::SharedPromiseState<void>::addOnResolve(std::function<void(std::optional<std::exception_ptr> exp)> callback) {
+    std::function<void()> task = [this, callback = std::move(callback)] {
         std::exception_ptr e = nullptr;
         {
             std::lock_guard<std::mutex> lck{mutex};
             e = exp;
         }
-        callback(!e, e);
+        callback(e);
     };
     addChain(task);
 }
 
 template<typename T>
 inline void celix::impl::SharedPromiseState<T>::addOnSuccessConsumeCallback(std::function<void(T)> callback) {
-    std::function<void()> task = [this, callback] {
+    std::function<void()> task = [this, callback = std::move(callback)] {
         if (isSuccessfullyResolved()) {
             callback(getValue());
         }
@@ -784,7 +814,7 @@ inline void celix::impl::SharedPromiseState<T>::addOnSuccessConsumeCallback(std:
 }
 
 inline void celix::impl::SharedPromiseState<void>::addOnSuccessConsumeCallback(std::function<void()> callback) {
-    std::function<void()> task = [this, callback] {
+    std::function<void()> task = [this, callback = std::move(callback)] {
         if (isSuccessfullyResolved()) {
             getValue();
             callback();
@@ -795,7 +825,7 @@ inline void celix::impl::SharedPromiseState<void>::addOnSuccessConsumeCallback(s
 
 template<typename T>
 inline void celix::impl::SharedPromiseState<T>::addOnFailureConsumeCallback(std::function<void(const std::exception&)> callback) {
-    std::function<void()> task = [this, callback] {
+    std::function<void()> task = [this, callback = std::move(callback)] {
         if (!isSuccessfullyResolved()) {
             try {
                 std::rethrow_exception(getFailure());
@@ -812,7 +842,7 @@ inline void celix::impl::SharedPromiseState<T>::addOnFailureConsumeCallback(std:
 }
 
 inline void celix::impl::SharedPromiseState<void>::addOnFailureConsumeCallback(std::function<void(const std::exception&)> callback) {
-    std::function<void()> task = [this, callback] {
+    std::function<void()> task = [this, callback = std::move(callback)] {
         if (!isSuccessfullyResolved()) {
             try {
                 std::rethrow_exception(getFailure());
diff --git a/misc/experimental/promise/gtest/src/PromiseTestSuite.cc b/misc/experimental/promise/gtest/src/PromiseTestSuite.cc
index 1b99c0d..dbed789 100644
--- a/misc/experimental/promise/gtest/src/PromiseTestSuite.cc
+++ b/misc/experimental/promise/gtest/src/PromiseTestSuite.cc
@@ -20,6 +20,7 @@
 #include <gtest/gtest.h>
 
 #include <future>
+#include <utility>
 
 #include "celix/PromiseFactory.h"
 
@@ -30,22 +31,70 @@ public:
     celix::PromiseFactory factory{ tbb::task_arena{5, 1} };
 };
 
+struct MovableInt {
+    MovableInt(int _val) : val(_val) {}
+    operator int() const { return val; }
 
+    int val;
+};
+
+struct NonMovableInt {
+    NonMovableInt(int _val) : val(_val) {}
+    operator int() const { return val; }
+    NonMovableInt(NonMovableInt&&) = delete;
+    NonMovableInt(const NonMovableInt&) = default;
+    NonMovableInt& operator=(NonMovableInt&&) = delete;
+    NonMovableInt& operator=(const NonMovableInt&) = default;
+
+    int val;
+};
+
+struct NonTrivialType {
+    NonTrivialType(std::string _val) : val(std::move(_val)) {}
+    operator std::string() const { return val; }
+    NonTrivialType(NonTrivialType&&) = default;
+    NonTrivialType(const NonTrivialType&) = default;
+    NonTrivialType& operator=(NonTrivialType&&) = default;
+    NonTrivialType& operator=(const NonTrivialType&) = default;
+
+
+    NonTrivialType(const char *c) : val(c) {}
+
+    NonTrivialType& operator=(const char *c) {
+        val = c;
+        return *this;
+    }
+
+    bool operator==(const char *c) const {
+        return c == val;
+    }
+
+    std::string val;
+};
+
+bool operator==( const char *c, const NonTrivialType &ntt) {
+    return c == ntt.val;
+}
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-result"
+#endif 
 
 TEST_F(PromiseTestSuite, simplePromise) {
-    auto deferred =  factory.deferred<long>();
-    std::thread t{[deferred] () mutable { //TODO TBD make deferred a shared_ptr to prevent need for mutable?
+    auto deferred =  factory.deferred<NonTrivialType>();
+    std::thread t{[&deferred] () {
         std::this_thread::sleep_for(std::chrono::milliseconds{50});
-        deferred.resolve(42);
+        deferred.resolve("test");
     }};
     auto promise = deferred.getPromise();
-    EXPECT_EQ(42, promise.getValue()); //block until ready
+    EXPECT_EQ("test", promise.getValue()); //block until ready
     EXPECT_TRUE(promise.isDone()); //got value, so promise is done
     EXPECT_ANY_THROW(promise.getFailure()); //succeeded, so no exception available
 
-    EXPECT_EQ(42, promise.getValue()); //note multiple call are valid;
+    EXPECT_EQ("test", promise.getValue()); //note multiple call are valid;
 
-    EXPECT_EQ(42, promise.moveValue()); //data is now moved
+    EXPECT_EQ("test", promise.moveOrGetValue()); //data is now moved
     EXPECT_THROW(promise.getValue(), celix::PromiseInvocationException); //data is already moved -> exception
     t.join();
 }
@@ -53,7 +102,7 @@ TEST_F(PromiseTestSuite, simplePromise) {
 TEST_F(PromiseTestSuite, failingPromise) {
     auto deferred =  factory.deferred<long>();
     auto cpy = deferred;
-    std::thread t{[deferred] () mutable {
+    std::thread t{[&deferred] () {
         deferred.fail(std::logic_error{"failing"});
     }};
     auto promise = deferred.getPromise();
@@ -371,4 +420,53 @@ TEST_F(PromiseTestSuite, failedResolvedWithPromiseFactory) {
     auto p2 = factory.resolved(42);
     EXPECT_TRUE(p2.isDone());
     EXPECT_EQ(42, p2.getValue());
-}
\ No newline at end of file
+}
+
+TEST_F(PromiseTestSuite, movableStruct) {
+    auto deferred =  factory.deferred<MovableInt>();
+    std::thread t{[&deferred] () {
+        std::this_thread::sleep_for(std::chrono::milliseconds{50});
+        deferred.resolve(42);
+    }};
+    auto promise = deferred.getPromise();
+    EXPECT_EQ(42, promise.getValue());
+    EXPECT_EQ(42, *promise);
+    t.join();
+}
+
+TEST_F(PromiseTestSuite, movableStructTemporary) {
+    auto deferred =  factory.deferred<MovableInt>();
+    std::thread t{[&deferred] () {
+        std::this_thread::sleep_for(std::chrono::milliseconds{50});
+        deferred.resolve(42);
+    }};
+    EXPECT_EQ(42, deferred.getPromise().getValue());
+    t.join();
+}
+
+TEST_F(PromiseTestSuite, nonMovableStruct) {
+    auto deferred =  factory.deferred<NonMovableInt>();
+    std::thread t{[&deferred] () {
+        std::this_thread::sleep_for(std::chrono::milliseconds{50});
+        deferred.resolve(42);
+    }};
+    auto promise = deferred.getPromise();
+    EXPECT_EQ(42, promise.getValue());
+    EXPECT_EQ(42, *promise);
+    t.join();
+}
+
+TEST_F(PromiseTestSuite, nonMovableStructTemporary) {
+    auto deferred =  factory.deferred<NonMovableInt>();
+    std::thread t{[&deferred] () {
+        std::this_thread::sleep_for(std::chrono::milliseconds{50});
+        deferred.resolve(42);
+    }};
+    EXPECT_EQ(42, deferred.getPromise().getValue());
+    t.join();
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
diff --git a/misc/experimental/promise/gtest/src/VoidPromiseTestSuite.cc b/misc/experimental/promise/gtest/src/VoidPromiseTestSuite.cc
index 679aaa5..56d1f73 100644
--- a/misc/experimental/promise/gtest/src/VoidPromiseTestSuite.cc
+++ b/misc/experimental/promise/gtest/src/VoidPromiseTestSuite.cc
@@ -30,11 +30,14 @@ public:
     celix::PromiseFactory factory{ tbb::task_arena{5, 1} };
 };
 
-
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-result"
+#endif
 
 TEST_F(VoidPromiseTestSuite, simplePromise) {
     auto deferred =  factory.deferred<void>();
-    std::thread t{[deferred] () mutable { //TODO TBD make deferred a shared_ptr to prevent need for mutable?
+    std::thread t{[&deferred] () {
         std::this_thread::sleep_for(std::chrono::milliseconds{50});
         deferred.resolve();
     }};
@@ -50,7 +53,7 @@ TEST_F(VoidPromiseTestSuite, simplePromise) {
 TEST_F(VoidPromiseTestSuite, failingPromise) {
     auto deferred =  factory.deferred<void>();
     auto cpy = deferred;
-    std::thread t{[deferred] () mutable {
+    std::thread t{[&deferred] () {
         deferred.fail(std::logic_error{"failing"});
     }};
     auto promise = deferred.getPromise();
@@ -349,4 +352,8 @@ TEST_F(VoidPromiseTestSuite, failedResolvedWithPromiseFactory) {
     auto p2 = factory.resolved();
     EXPECT_TRUE(p2.isDone());
     EXPECT_TRUE(p2.getValue());
-}
\ No newline at end of file
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
diff --git a/misc/experimental/promise/src/PromiseExamples.cc b/misc/experimental/promise/src/PromiseExamples.cc
index d344381..de7d5f7 100644
--- a/misc/experimental/promise/src/PromiseExamples.cc
+++ b/misc/experimental/promise/src/PromiseExamples.cc
@@ -68,4 +68,4 @@ int main() {
     });
     p1.wait();
     p2.wait();
-}
\ No newline at end of file
+}