// yabridge: a Wine plugin bridge // Copyright (C) 2020-2024 Robbert van der Helm // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #pragma once #include namespace bitsery { namespace ext { /** * A temporary replacement for `bitsery::ext::StdVariant` to avoid * reinitializing the object we're deserializing into if the requested variant * is currently active. For storing audio buffers we use variants containing * float and double vectors to have a type safe way to disambiguate between * single and double precision audio, but as it turns out bitsery's * `std::variant` extension would always reinitialize those objects, undoing our * efforts to prevent allocations. */ template class InPlaceVariant : public StdVariant { public: template void deserialize(Des& des, std::variant& obj, Fnc&&) const { size_t index{}; details::readSize( des.adapter(), index, sizeof...(Ts), std::integral_constant{}); // Most of this is copied directly from the original implementation. // We just added the check here to reuse the existing object if // possible. this->execIndex(index, obj, [this, &des](auto& data, auto index) { constexpr size_t Index = decltype(index)::value; using TElem = typename std::variant_alternative>::type; // Reinitializing nontrivial types may be expensive especially when // they reference heap data, so if `data` is already holding the // requested variant then we'll deserialize into the existing object if constexpr (!std::is_trivial_v) { if (auto item = std::get_if(&data)) { this->serializeType(des, *item); return; } } TElem item = ::bitsery::Access::create(); this->serializeType(des, item); data = std::variant(std::in_place_index_t{}, std::move(item)); }); } }; template InPlaceVariant(Overloads...) -> InPlaceVariant; } // namespace ext namespace traits { template struct ExtensionTraits, Variant> { static_assert( bitsery::details::IsSpecializationOf::value, "InPlaceVariant only works with std::variant"); using TValue = void; static constexpr bool SupportValueOverload = false; static constexpr bool SupportObjectOverload = true; static constexpr bool SupportLambdaOverload = false; }; } // namespace traits } // namespace bitsery