diff --git a/include/openPMD/auxiliary/Memory.hpp b/include/openPMD/auxiliary/Memory.hpp index 75199f0985..19c36a1ee6 100644 --- a/include/openPMD/auxiliary/Memory.hpp +++ b/include/openPMD/auxiliary/Memory.hpp @@ -22,15 +22,13 @@ #include "openPMD/Dataset.hpp" #include "openPMD/Datatype.hpp" +#include "openPMD/Error.hpp" #include "openPMD/auxiliary/UniquePtr.hpp" -#include +#include #include -#include #include -#include #include -#include namespace openPMD { @@ -48,30 +46,58 @@ namespace auxiliary */ struct WriteBuffer { - using EligibleTypes = std:: - variant, UniquePtrWithLambda>; - EligibleTypes m_buffer; + /* + * Sic. Have to put the unique_ptr behind a shared_ptr because + * std::variant does not want non-copyable types. + * Use a separate class to avoid mistakes in double dereference. + */ + struct CopyableUniquePtr + : private std::shared_ptr> + { + private: + using parent_t = std::shared_ptr>; - WriteBuffer(); + public: + CopyableUniquePtr(); + CopyableUniquePtr(UniquePtrWithLambda ptr_in); + auto get() -> void *; + [[nodiscard]] auto get() const -> void const *; + [[nodiscard]] auto release() -> UniquePtrWithLambda; + }; + using SharedPtr = std::shared_ptr; + /* + * Use std::any publically since some compilers have trouble with + * certain uses of std::variant, so hide it from them. + * Look into Memory_internal.hpp for the variant type. + * https://github.com/openPMD/openPMD-api/issues/1720 + */ + std::any m_buffer; - template - explicit WriteBuffer(Args &&...args) - : m_buffer(std::forward(args)...) - {} + WriteBuffer(); + WriteBuffer(std::shared_ptr ptr); + WriteBuffer(UniquePtrWithLambda ptr); - WriteBuffer(WriteBuffer &&) noexcept( - noexcept(EligibleTypes(std::declval()))); + WriteBuffer(WriteBuffer &&) noexcept; WriteBuffer(WriteBuffer const &) = delete; - WriteBuffer &operator=(WriteBuffer &&) noexcept(noexcept( - std::declval() = - std::declval())); + WriteBuffer &operator=(WriteBuffer &&) noexcept; WriteBuffer &operator=(WriteBuffer const &) = delete; WriteBuffer const &operator=(std::shared_ptr ptr); - - WriteBuffer const &operator=(UniquePtrWithLambda ptr); + WriteBuffer const &operator=(UniquePtrWithLambda ptr); void const *get() const; + + template + auto as_variant() -> variant_t & + { + return *std::any_cast(&m_buffer); + } + + template + auto as_variant() const -> variant_t const & + { + return *std::any_cast(&m_buffer); + } }; } // namespace auxiliary } // namespace openPMD diff --git a/include/openPMD/auxiliary/Memory_internal.hpp b/include/openPMD/auxiliary/Memory_internal.hpp new file mode 100644 index 0000000000..bf3c8ccb4b --- /dev/null +++ b/include/openPMD/auxiliary/Memory_internal.hpp @@ -0,0 +1,30 @@ +/* Copyright 2025 Franz Poeschel + * + * This file is part of openPMD-api. + * + * openPMD-api is free software: you can redistribute it and/or modify + * it under the terms of of either the GNU General Public License or + * the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * openPMD-api 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 and the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * and the GNU Lesser General Public License along with openPMD-api. + * If not, see . + */ +#pragma once + +#include "openPMD/auxiliary/Memory.hpp" + +namespace openPMD::auxiliary +{ +// cannot use a unique_ptr inside a std::variant, so we represent it with this +using WriteBufferTypes = + std::variant; +} // namespace openPMD::auxiliary diff --git a/include/openPMD/openPMD.hpp b/include/openPMD/openPMD.hpp index bc6ea782f7..268ddaa491 100644 --- a/include/openPMD/openPMD.hpp +++ b/include/openPMD/openPMD.hpp @@ -25,21 +25,6 @@ namespace openPMD {} -#if defined(CUDA_VERSION) && !defined(OPENPMD_SKIP_CHECK_ISSUE_1720) -static_assert(__cplusplus < 202002L || CUDA_VERSION >= 12040, R"( -Cannot use the openPMD-api in C++20 projects under a Cuda version lower -than 12.4.0 due to a bug in the implementation of std::variant. -Further information at: -https://github.com/openPMD/openPMD-api/issues/1720 -https://forums.developer.nvidia.com/t/nvcc-c-20-std-variant-complie-failed/270162/5 -This cannot be fixed on our side, please either upgrade to CUDA >= 12.4.0 -or use C++17. -If you think that this assertion is shown wrongly, please apply -'#define OPENPMD_SKIP_CHECK_ISSUE_1720' before including -''. -)"); -#endif - // IWYU pragma: begin_exports #include "openPMD/Dataset.hpp" #include "openPMD/Datatype.hpp" diff --git a/src/IO/ADIOS/ADIOS2File.cpp b/src/IO/ADIOS/ADIOS2File.cpp index 30d6f0e981..1d20aeb04d 100644 --- a/src/IO/ADIOS/ADIOS2File.cpp +++ b/src/IO/ADIOS/ADIOS2File.cpp @@ -26,6 +26,8 @@ #include "openPMD/IO/AbstractIOHandler.hpp" #include "openPMD/IterationEncoding.hpp" #include "openPMD/auxiliary/Environment.hpp" +#include "openPMD/auxiliary/Memory.hpp" +#include "openPMD/auxiliary/Memory_internal.hpp" #include "openPMD/auxiliary/StringManip.hpp" #include @@ -114,22 +116,13 @@ void WriteDataset::call(ADIOS2File &ba, detail::BufferedPut &bp) } else if constexpr (std::is_same_v< ptr_type, - UniquePtrWithLambda>) + auxiliary::WriteBuffer::CopyableUniquePtr>) { BufferedUniquePtrPut bput; bput.name = std::move(bp.name); bput.offset = std::move(bp.param.offset); bput.extent = std::move(bp.param.extent); - /* - * Note: Moving is required here since it's a unique_ptr. - * std::forward<>() would theoretically work, but it - * requires the type parameter and we don't have that - * inside the lambda. - * (ptr_type does not work for this case). - */ - // clang-format off - bput.data = std::move(arg); // NOLINT(bugprone-move-forwarding-reference) - // clang-format on + bput.data = arg.release(); bput.dtype = bp.param.dtype; ba.m_uniquePtrPuts.push_back(std::move(bput)); } @@ -139,7 +132,7 @@ void WriteDataset::call(ADIOS2File &ba, detail::BufferedPut &bp) always_false_v, "Unhandled std::variant branch"); } }, - bp.param.data.m_buffer); + bp.param.data.as_variant()); } template diff --git a/src/auxiliary/Memory.cpp b/src/auxiliary/Memory.cpp index cf95ecf582..d60523b2e6 100644 --- a/src/auxiliary/Memory.cpp +++ b/src/auxiliary/Memory.cpp @@ -20,7 +20,11 @@ */ #include "openPMD/auxiliary/Memory.hpp" +#include "openPMD/ChunkInfo.hpp" +#include "openPMD/auxiliary/Memory_internal.hpp" +#include "openPMD/auxiliary/UniquePtr.hpp" +#include #include #include #include @@ -157,24 +161,58 @@ allocatePtr(Datatype dtype, Extent const &e) return allocatePtr(dtype, numPoints); } -WriteBuffer::WriteBuffer() : m_buffer(UniquePtrWithLambda()) +WriteBuffer::CopyableUniquePtr::CopyableUniquePtr() = default; + +WriteBuffer::CopyableUniquePtr::CopyableUniquePtr( + UniquePtrWithLambda ptr_in) + : parent_t{std::make_shared>(std::move(ptr_in))} {} -WriteBuffer::WriteBuffer(WriteBuffer &&) noexcept( - noexcept(EligibleTypes(std::declval()))) = default; -WriteBuffer &WriteBuffer::operator=(WriteBuffer &&) noexcept(noexcept( - std::declval() = std::declval())) = - default; +auto WriteBuffer::CopyableUniquePtr::get() -> void * +{ + return (**this).get(); +} + +auto WriteBuffer::CopyableUniquePtr::get() const -> void const * +{ + return (**this).get(); +} + +auto WriteBuffer::CopyableUniquePtr::release() -> UniquePtrWithLambda +{ + if (parent_t::use_count() > 1) + { + throw error::Internal( + "Control flow error: UniquePtr variant of WriteBuffer " + "has been copied."); + } + UniquePtrWithLambda res = std::move(**this); + this->reset(); + return res; +} + +WriteBuffer::WriteBuffer() : m_buffer(std::make_any()) +{} +WriteBuffer::WriteBuffer(std::shared_ptr ptr) + : m_buffer(std::make_any(std::move(ptr))) +{} +WriteBuffer::WriteBuffer(UniquePtrWithLambda ptr) + : m_buffer( + std::make_any(CopyableUniquePtr(std::move(ptr)))) +{} + +WriteBuffer::WriteBuffer(WriteBuffer &&) noexcept = default; +WriteBuffer &WriteBuffer::operator=(WriteBuffer &&) noexcept = default; WriteBuffer const &WriteBuffer::operator=(std::shared_ptr ptr) { - m_buffer = std::move(ptr); + m_buffer = std::make_any(std::move(ptr)); return *this; } - -WriteBuffer const &WriteBuffer::operator=(UniquePtrWithLambda ptr) +WriteBuffer const &WriteBuffer::operator=(UniquePtrWithLambda ptr) { - m_buffer = std::move(ptr); + m_buffer = + std::make_any(CopyableUniquePtr(std::move(ptr))); return *this; } @@ -186,6 +224,6 @@ void const *WriteBuffer::get() const // we're being sneaky and don't distinguish the types here return static_cast(arg.get()); }, - m_buffer); + as_variant()); } } // namespace openPMD::auxiliary diff --git a/test/CoreTest.cpp b/test/CoreTest.cpp index 6c62e1d82e..e4a8d17c70 100644 --- a/test/CoreTest.cpp +++ b/test/CoreTest.cpp @@ -11,7 +11,7 @@ #include "openPMD/IO/ADIOS/macros.hpp" #include "openPMD/auxiliary/Filesystem.hpp" -#include "openPMD/auxiliary/JSON.hpp" +#include "openPMD/auxiliary/Memory_internal.hpp" #include "openPMD/auxiliary/UniquePtr.hpp" #include @@ -1252,7 +1252,7 @@ TEST_CASE("use_count_test", "[core]") std::get>( static_cast *>( pprc.get().m_chunks.front().parameter.get()) - ->data.m_buffer) + ->data.as_variant()) .use_count() == 1); #endif }