LCOV - code coverage report
Current view: top level - muse/framework/global/functional - inplace_function_mv.h Coverage Total Hit
Test: filtered_coverage.info Lines: 75.0 % 16 12
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2026 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #pragma once
      23              : 
      24              : #include <cstddef>
      25              : #include <functional>
      26              : #include <new>
      27              : #include <type_traits>
      28              : #include <utility>
      29              : 
      30              : namespace muse::functional {
      31              : template<typename Signature, size_t Capacity = 32, size_t Alignment = alignof(std::max_align_t)>
      32              : class MoveOnlyInplaceFunction;
      33              : 
      34              : namespace detail {
      35              : template<typename>
      36              : struct IsMoveOnlyInplaceFunction : std::false_type {};
      37              : 
      38              : template<typename Signature, size_t Capacity, size_t Alignment>
      39              : struct IsMoveOnlyInplaceFunction<MoveOnlyInplaceFunction<Signature, Capacity, Alignment> > : std::true_type {};
      40              : 
      41              : template<typename Ret, typename ... Args>
      42              : struct MoveOnlyInplaceFunctionVTable
      43              : {
      44              :     using Invoke = Ret (*)(void*, Args&&...);
      45              :     using Relocate = void (*)(void*, void*) noexcept;
      46              :     using Destroy = void (*)(void*) noexcept;
      47              : 
      48              :     Invoke invoke = nullptr;
      49              :     Relocate relocate = nullptr;
      50              :     Destroy destroy = nullptr;
      51              : };
      52              : 
      53              : template<typename Callable, typename Ret, typename ... Args>
      54              : Ret invokeMoveOnlyInplaceFunction(void* storage, Args&&... args)
      55              : {
      56              :     if constexpr (std::is_void_v<Ret>) {
      57              :         (*static_cast<Callable*>(storage))(std::forward<Args>(args)...);
      58              :     } else {
      59              :         return (*static_cast<Callable*>(storage))(std::forward<Args>(args)...);
      60              :     }
      61              : }
      62              : 
      63              : template<typename Callable>
      64              : void relocateMoveOnlyInplaceFunction(void* destination, void* source) noexcept
      65              : {
      66              :     auto* callable = static_cast<Callable*>(source);
      67              :     new (destination) Callable(std::move(*callable));
      68              :     callable->~Callable();
      69              : }
      70              : 
      71              : template<typename Callable>
      72              : void destroyMoveOnlyInplaceFunction(void* storage) noexcept
      73              : {
      74              :     static_cast<Callable*>(storage)->~Callable();
      75              : }
      76              : 
      77              : template<typename Callable, typename Ret, typename ... Args>
      78              : const MoveOnlyInplaceFunctionVTable<Ret, Args...>* moveOnlyInplaceFunctionVTable() noexcept
      79              : {
      80              :     static const MoveOnlyInplaceFunctionVTable<Ret, Args...> vtable {
      81              :         &invokeMoveOnlyInplaceFunction<Callable, Ret, Args...>,
      82              :         &relocateMoveOnlyInplaceFunction<Callable>,
      83              :         &destroyMoveOnlyInplaceFunction<Callable>
      84              :     };
      85              : 
      86              :     return &vtable;
      87              : }
      88              : } // namespace detail
      89              : 
      90              : template<typename Ret, typename ... Args, size_t Capacity, size_t Alignment>
      91              : class MoveOnlyInplaceFunction<Ret(Args...), Capacity, Alignment>
      92              : {
      93              : public:
      94           48 :     MoveOnlyInplaceFunction() noexcept = default;
      95              : 
      96              :     MoveOnlyInplaceFunction(std::nullptr_t) noexcept {}
      97              : 
      98              :     MoveOnlyInplaceFunction(const MoveOnlyInplaceFunction&) = delete;
      99              :     MoveOnlyInplaceFunction& operator=(const MoveOnlyInplaceFunction&) = delete;
     100              : 
     101              :     MoveOnlyInplaceFunction(MoveOnlyInplaceFunction&& other) noexcept
     102              :     {
     103              :         moveFrom(std::move(other));
     104              :     }
     105              : 
     106              :     MoveOnlyInplaceFunction& operator=(MoveOnlyInplaceFunction&& other) noexcept
     107              :     {
     108              :         if (this != &other) {
     109              :             reset();
     110              :             moveFrom(std::move(other));
     111              :         }
     112              : 
     113              :         return *this;
     114              :     }
     115              : 
     116              :     template<typename Callable,
     117              :              typename Fn = std::decay_t<Callable>,
     118              :              std::enable_if_t<!detail::IsMoveOnlyInplaceFunction<Fn>::value
     119              :                               && std::is_invocable_r_v<Ret, Fn&, Args...>, int> = 0>
     120              :     MoveOnlyInplaceFunction(Callable&& callable)
     121              :     {
     122              :         static_assert(sizeof(Fn) <= Capacity, "Callable is too large for this MoveOnlyInplaceFunction");
     123              :         static_assert(alignof(Fn) <= Alignment, "Callable alignment is too large for this MoveOnlyInplaceFunction");
     124              :         static_assert(std::is_nothrow_move_constructible_v<Fn>,
     125              :                       "MoveOnlyInplaceFunction requires nothrow move-constructible callables");
     126              : 
     127              :         m_vtable = detail::moveOnlyInplaceFunctionVTable<Fn, Ret, Args...>();
     128              :         new (storage()) Fn(std::forward<Callable>(callable));
     129              :     }
     130              : 
     131              :     template<typename Callable,
     132              :              typename Fn = std::decay_t<Callable>,
     133              :              std::enable_if_t<!detail::IsMoveOnlyInplaceFunction<Fn>::value
     134              :                               && std::is_invocable_r_v<Ret, Fn&, Args...>, int> = 0>
     135              :     MoveOnlyInplaceFunction& operator=(Callable&& callable)
     136              :     {
     137              :         *this = MoveOnlyInplaceFunction(std::forward<Callable>(callable));
     138              :         return *this;
     139              :     }
     140              : 
     141           46 :     MoveOnlyInplaceFunction& operator=(std::nullptr_t) noexcept
     142              :     {
     143           46 :         reset();
     144           45 :         return *this;
     145              :     }
     146              : 
     147           47 :     ~MoveOnlyInplaceFunction() noexcept
     148              :     {
     149           47 :         reset();
     150           47 :     }
     151              : 
     152              :     Ret operator()(Args... args) const
     153              :     {
     154              :         if (!m_vtable) {
     155              :             throw std::bad_function_call();
     156              :         }
     157              : 
     158              :         if constexpr (std::is_void_v<Ret>) {
     159              :             m_vtable->invoke(storage(), std::forward<Args>(args)...);
     160              :         } else {
     161              :             return m_vtable->invoke(storage(), std::forward<Args>(args)...);
     162              :         }
     163              :     }
     164              : 
     165           45 :     explicit operator bool() const noexcept
     166              :     {
     167           45 :         return m_vtable != nullptr;
     168              :     }
     169              : 
     170           91 :     void reset() noexcept
     171              :     {
     172           91 :         if (!m_vtable) {
     173           91 :             return;
     174              :         }
     175              : 
     176            0 :         m_vtable->destroy(storage());
     177            0 :         m_vtable = nullptr;
     178              :     }
     179              : 
     180              : private:
     181              :     using VTable = detail::MoveOnlyInplaceFunctionVTable<Ret, Args...>;
     182              :     using Storage = std::aligned_storage_t<Capacity, Alignment>;
     183              : 
     184            0 :     void* storage() const noexcept
     185              :     {
     186            0 :         return const_cast<Storage*>(&m_storage);
     187              :     }
     188              : 
     189              :     void moveFrom(MoveOnlyInplaceFunction&& other) noexcept
     190              :     {
     191              :         if (!other.m_vtable) {
     192              :             return;
     193              :         }
     194              : 
     195              :         m_vtable = other.m_vtable;
     196              :         m_vtable->relocate(storage(), other.storage());
     197              :         other.m_vtable = nullptr;
     198              :     }
     199              : 
     200              :     const VTable* m_vtable = nullptr;
     201              :     mutable Storage m_storage {};
     202              : };
     203              : 
     204              : template<typename Signature, size_t Capacity, size_t Alignment>
     205              : bool operator==(const MoveOnlyInplaceFunction<Signature, Capacity, Alignment>& function, std::nullptr_t) noexcept
     206              : {
     207              :     return !function;
     208              : }
     209              : 
     210              : template<typename Signature, size_t Capacity, size_t Alignment>
     211              : bool operator!=(const MoveOnlyInplaceFunction<Signature, Capacity, Alignment>& function, std::nullptr_t) noexcept
     212              : {
     213              :     return bool(function);
     214              : }
     215              : } // namespace muse::functional
        

Generated by: LCOV version 2.0-1