File indexing completed on 2025-01-19 04:19:12

0001 /*
0002     SPDX-FileCopyrightText: 2014-2015 Daniel Vrátil <dvratil@redhat.com>
0003     SPDX-FileCopyrightText: 2016-2019 Daniel Vrátil <dvratil@kde.org>
0004     SPDX-FileCopyrightText: 2016 Christian Mollekopf <mollekopf@kolabsystems.com>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #ifndef KASYNC_CONTINUATIONS_P_H_
0010 #define KASYNC_CONTINUATIONS_P_H_
0011 
0012 #include <limits>
0013 #include <functional>
0014 #include <type_traits>
0015 
0016 namespace KAsync
0017 {
0018 
0019 template<typename Out, typename ... In>
0020 class Job;
0021 
0022 template<typename T>
0023 class Future;
0024 
0025 struct Error;
0026 
0027 //@cond PRIVATE
0028 namespace detail {
0029 template<typename T>
0030 struct identity {
0031     using type = T;
0032 };
0033 template<typename T>
0034 using identity_t = typename identity<T>::type;
0035 }
0036 //@endcond
0037 
0038 template<typename Out, typename ... In>
0039 using AsyncContinuation = detail::identity_t<std::function<void(In ..., KAsync::Future<Out>&)>>;
0040 
0041 template<typename Out, typename ... In>
0042 using AsyncErrorContinuation = detail::identity_t<std::function<void(const KAsync::Error &, In ..., KAsync::Future<Out>&)>>;
0043 
0044 template<typename Out, typename ... In>
0045 using SyncContinuation = detail::identity_t<std::function<Out(In ...)>>;
0046 
0047 template<typename Out, typename ... In>
0048 using SyncErrorContinuation = detail::identity_t<std::function<Out(const KAsync::Error &, In ...)>>;
0049 
0050 template<typename Out, typename ... In>
0051 using JobContinuation = detail::identity_t<std::function<KAsync::Job<Out>(In ...)>>;
0052 
0053 template<typename Out, typename ... In>
0054 using JobErrorContinuation = detail::identity_t<std::function<KAsync::Job<Out>(const KAsync::Error &, In ...)>>;
0055 
0056 //@cond PRIVATE
0057 namespace Private
0058 {
0059 /**
0060  * FIXME: This should be a simple alias to std::variant once we can depend on C++17.
0061  */
0062 template<typename Out, typename ... In>
0063 struct ContinuationHolder
0064 {
0065 #ifndef KASYNC_TEST
0066 private:
0067 #endif
0068     using Tuple = std::tuple<
0069         AsyncContinuation<Out, In ...>,
0070         AsyncErrorContinuation<Out, In ...>,
0071         SyncContinuation<Out, In ...>,
0072         SyncErrorContinuation<Out, In ...>,
0073         JobContinuation<Out, In ...>,
0074         JobErrorContinuation<Out, In ...>
0075     >;
0076 
0077     template<typename T>
0078     struct tuple_max;
0079 
0080     template<typename T>
0081     struct tuple_max<std::tuple<T>> {
0082         static constexpr std::size_t size = sizeof(T);
0083         static constexpr std::size_t alignment = alignof(T);
0084     };
0085     template<typename T, typename ... Types>
0086     struct tuple_max<std::tuple<T, Types ...>> {
0087         static constexpr std::size_t size = std::max(sizeof(T), tuple_max<std::tuple<Types ...>>::size);
0088         static constexpr std::size_t alignment = std::max(alignof(T), tuple_max<std::tuple<Types ...>>::alignment);
0089     };
0090 
0091     template<typename T, typename Tuple>
0092     struct tuple_index;
0093 
0094     template<typename T, typename ... Types>
0095     struct tuple_index<T, std::tuple<T, Types ...>> {
0096         static constexpr std::size_t value = 0;
0097     };
0098     template<typename T, typename U, typename ... Types>
0099     struct tuple_index<T, std::tuple<U, Types ...>> {
0100         static constexpr std::size_t value = tuple_index<T, std::tuple<Types ...>>::value + 1;
0101     };
0102 
0103     template<typename T>
0104     inline static void move_helper(void *storage, void *data) {
0105         new (storage) T(std::move(*reinterpret_cast<T*>(data)));
0106     }
0107 
0108     template<typename T>
0109     inline static void destroy_helper(void *storage) {
0110         reinterpret_cast<T*>(storage)->~T();
0111     }
0112 
0113     template<typename Tuple,
0114              std::size_t I = std::tuple_size<Tuple>::value - 1>
0115     struct storage_helper {
0116         using T = std::tuple_element_t<I, Tuple>;
0117         inline static void move(std::size_t index, void *storage, void *data) {
0118             if (I == index) {
0119                 move_helper<T>(storage, data);
0120             } else {
0121                 storage_helper<Tuple, I - 1>::move(index, storage, data);
0122             }
0123         }
0124         inline static void destroy(std::size_t index, void *storage) {
0125             if (I == index) {
0126                 destroy_helper<T>(storage);
0127             } else {
0128                 storage_helper<Tuple, I - 1>::destroy(index, storage);
0129             }
0130         }
0131     };
0132 
0133     template<typename Tuple>
0134     struct storage_helper<Tuple, 0> {
0135         using T = std::tuple_element_t<0, Tuple>;
0136         inline static void move(std::size_t, void *storage, void *data) {
0137             move_helper<T>(storage, data);
0138         }
0139         inline static void destroy(std::size_t, void *storage) {
0140             destroy_helper<T>(storage);
0141         }
0142     };
0143 
0144     enum {
0145         Invalid = std::numeric_limits<std::size_t>::max() - 1
0146     };
0147 
0148     std::size_t mIndex = Invalid;
0149     std::aligned_storage_t<tuple_max<Tuple>::size, tuple_max<Tuple>::alignment> mStorage = {};
0150 
0151 public:
0152     #define KASYNC_P_DEFINE_CONSTRUCTOR(type) \
0153         ContinuationHolder(type<Out, In ...> &&cont) \
0154             : mIndex(tuple_index<type<Out, In ...>, Tuple>::value) \
0155         { \
0156             move_helper<type<Out, In ...>>(&mStorage, &cont); \
0157         }
0158     KASYNC_P_DEFINE_CONSTRUCTOR(AsyncContinuation)
0159     KASYNC_P_DEFINE_CONSTRUCTOR(AsyncErrorContinuation)
0160     KASYNC_P_DEFINE_CONSTRUCTOR(SyncContinuation)
0161     KASYNC_P_DEFINE_CONSTRUCTOR(SyncErrorContinuation)
0162     KASYNC_P_DEFINE_CONSTRUCTOR(JobContinuation)
0163     KASYNC_P_DEFINE_CONSTRUCTOR(JobErrorContinuation)
0164     #undef KASYNC_P_DEFINE_CONSTRUCTOR
0165 
0166     ContinuationHolder(ContinuationHolder &&other) noexcept {
0167         std::swap(mIndex, other.mIndex);
0168         storage_helper<Tuple>::move(mIndex, &mStorage, &other.mStorage);
0169     }
0170 
0171     ContinuationHolder &operator=(ContinuationHolder &&other) noexcept {
0172         std::swap(mIndex, other.mIndex);
0173         storage_helper<Tuple>::move(mIndex, &mStorage, &other.mStorage);
0174         return *this;
0175     }
0176 
0177     ContinuationHolder(const ContinuationHolder &) = delete;
0178     ContinuationHolder &operator=(const ContinuationHolder &) = delete;
0179 
0180     ~ContinuationHolder() {
0181         if (mIndex != Invalid) {
0182             storage_helper<Tuple>::destroy(mIndex, &mStorage);
0183             mIndex = Invalid;
0184         }
0185     }
0186 
0187     template<typename T>
0188     inline bool is() const {
0189         return mIndex == tuple_index<T, Tuple>::value;
0190     }
0191 
0192     template<typename T>
0193     inline const T &get() const {
0194         if (!is<T>()) {
0195             throw std::bad_cast();
0196         }
0197         return *reinterpret_cast<const T *>(&mStorage);
0198     }
0199 
0200     template<typename T>
0201     inline T &&get() {
0202         if (!is<T>()) {
0203             throw std::bad_cast();
0204         }
0205         return std::move(*reinterpret_cast<T *>(&mStorage));
0206     }
0207 };
0208 
0209 template<typename T, typename Holder>
0210 inline bool continuationIs(const Holder &holder) {
0211     return holder.template is<T>();
0212 }
0213 
0214 template<typename T, typename Holder>
0215 inline const T &continuationGet(const Holder &holder) {
0216     return holder.template get<T>();
0217 }
0218 
0219 template<typename T, typename Holder>
0220 inline T &&continuationGet(Holder &holder) {
0221     return holder.template get<T>();
0222 }
0223 
0224 } // namespace Private
0225 //@endcond
0226 
0227 
0228 }
0229 
0230 
0231 #endif