Skip to content

Commit 0dc2d71

Browse files
Fix Variant issue with certain Cuda versions (#1807)
* Remove variant from public interface of Memory.hpp This should fix the Cuda issues. Will not yet work with ADIOS2 * Remove include guard * Fix ADIOS2 * Fix invasive test * comments * Use own class for MovableUniquePtr * extract definitions * Cleanup * Rename MovableUniquePtr --> CopyableUniquePtr
1 parent feded2c commit 0dc2d71

6 files changed

Lines changed: 131 additions & 59 deletions

File tree

include/openPMD/auxiliary/Memory.hpp

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,13 @@
2222

2323
#include "openPMD/Dataset.hpp"
2424
#include "openPMD/Datatype.hpp"
25+
#include "openPMD/Error.hpp"
2526
#include "openPMD/auxiliary/UniquePtr.hpp"
2627

27-
#include <complex>
28+
#include <any>
2829
#include <functional>
29-
#include <iostream>
3030
#include <memory>
31-
#include <type_traits>
3231
#include <utility>
33-
#include <variant>
3432

3533
namespace openPMD
3634
{
@@ -48,30 +46,58 @@ namespace auxiliary
4846
*/
4947
struct WriteBuffer
5048
{
51-
using EligibleTypes = std::
52-
variant<std::shared_ptr<void const>, UniquePtrWithLambda<void>>;
53-
EligibleTypes m_buffer;
49+
/*
50+
* Sic. Have to put the unique_ptr behind a shared_ptr because
51+
* std::variant does not want non-copyable types.
52+
* Use a separate class to avoid mistakes in double dereference.
53+
*/
54+
struct CopyableUniquePtr
55+
: private std::shared_ptr<UniquePtrWithLambda<void>>
56+
{
57+
private:
58+
using parent_t = std::shared_ptr<UniquePtrWithLambda<void>>;
5459

55-
WriteBuffer();
60+
public:
61+
CopyableUniquePtr();
62+
CopyableUniquePtr(UniquePtrWithLambda<void> ptr_in);
63+
auto get() -> void *;
64+
[[nodiscard]] auto get() const -> void const *;
65+
[[nodiscard]] auto release() -> UniquePtrWithLambda<void>;
66+
};
67+
using SharedPtr = std::shared_ptr<void const>;
68+
/*
69+
* Use std::any publically since some compilers have trouble with
70+
* certain uses of std::variant, so hide it from them.
71+
* Look into Memory_internal.hpp for the variant type.
72+
* https://github.com/openPMD/openPMD-api/issues/1720
73+
*/
74+
std::any m_buffer;
5675

57-
template <typename... Args>
58-
explicit WriteBuffer(Args &&...args)
59-
: m_buffer(std::forward<Args>(args)...)
60-
{}
76+
WriteBuffer();
77+
WriteBuffer(std::shared_ptr<void const> ptr);
78+
WriteBuffer(UniquePtrWithLambda<void> ptr);
6179

62-
WriteBuffer(WriteBuffer &&) noexcept(
63-
noexcept(EligibleTypes(std::declval<EligibleTypes &&>())));
80+
WriteBuffer(WriteBuffer &&) noexcept;
6481
WriteBuffer(WriteBuffer const &) = delete;
65-
WriteBuffer &operator=(WriteBuffer &&) noexcept(noexcept(
66-
std::declval<EligibleTypes &>() =
67-
std::declval<EligibleTypes &&>()));
82+
WriteBuffer &operator=(WriteBuffer &&) noexcept;
6883
WriteBuffer &operator=(WriteBuffer const &) = delete;
6984

7085
WriteBuffer const &operator=(std::shared_ptr<void const> ptr);
71-
72-
WriteBuffer const &operator=(UniquePtrWithLambda<void const> ptr);
86+
WriteBuffer const &operator=(UniquePtrWithLambda<void> ptr);
7387

7488
void const *get() const;
89+
90+
template <typename variant_t>
91+
auto as_variant() -> variant_t &
92+
{
93+
return *std::any_cast<variant_t>(&m_buffer);
94+
}
95+
96+
template <typename variant_t>
97+
auto as_variant() const -> variant_t const &
98+
{
99+
return *std::any_cast<variant_t>(&m_buffer);
100+
}
75101
};
76102
} // namespace auxiliary
77103
} // namespace openPMD
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* Copyright 2025 Franz Poeschel
2+
*
3+
* This file is part of openPMD-api.
4+
*
5+
* openPMD-api is free software: you can redistribute it and/or modify
6+
* it under the terms of of either the GNU General Public License or
7+
* the GNU Lesser General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* openPMD-api is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License and the GNU Lesser General Public License
15+
* for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* and the GNU Lesser General Public License along with openPMD-api.
19+
* If not, see <http://www.gnu.org/licenses/>.
20+
*/
21+
#pragma once
22+
23+
#include "openPMD/auxiliary/Memory.hpp"
24+
25+
namespace openPMD::auxiliary
26+
{
27+
// cannot use a unique_ptr inside a std::variant, so we represent it with this
28+
using WriteBufferTypes =
29+
std::variant<WriteBuffer::CopyableUniquePtr, WriteBuffer::SharedPtr>;
30+
} // namespace openPMD::auxiliary

include/openPMD/openPMD.hpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,6 @@
2525
namespace openPMD
2626
{}
2727

28-
#if defined(CUDA_VERSION) && !defined(OPENPMD_SKIP_CHECK_ISSUE_1720)
29-
static_assert(__cplusplus < 202002L || CUDA_VERSION >= 12040, R"(
30-
Cannot use the openPMD-api in C++20 projects under a Cuda version lower
31-
than 12.4.0 due to a bug in the implementation of std::variant.
32-
Further information at:
33-
https://github.com/openPMD/openPMD-api/issues/1720
34-
https://forums.developer.nvidia.com/t/nvcc-c-20-std-variant-complie-failed/270162/5
35-
This cannot be fixed on our side, please either upgrade to CUDA >= 12.4.0
36-
or use C++17.
37-
If you think that this assertion is shown wrongly, please apply
38-
'#define OPENPMD_SKIP_CHECK_ISSUE_1720' before including
39-
'<openPMD/openPMD.hpp>'.
40-
)");
41-
#endif
42-
4328
// IWYU pragma: begin_exports
4429
#include "openPMD/Dataset.hpp"
4530
#include "openPMD/Datatype.hpp"

src/IO/ADIOS/ADIOS2File.cpp

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "openPMD/IO/AbstractIOHandler.hpp"
2727
#include "openPMD/IterationEncoding.hpp"
2828
#include "openPMD/auxiliary/Environment.hpp"
29+
#include "openPMD/auxiliary/Memory.hpp"
30+
#include "openPMD/auxiliary/Memory_internal.hpp"
2931
#include "openPMD/auxiliary/StringManip.hpp"
3032

3133
#include <cstdint>
@@ -114,22 +116,13 @@ void WriteDataset::call(ADIOS2File &ba, detail::BufferedPut &bp)
114116
}
115117
else if constexpr (std::is_same_v<
116118
ptr_type,
117-
UniquePtrWithLambda<void>>)
119+
auxiliary::WriteBuffer::CopyableUniquePtr>)
118120
{
119121
BufferedUniquePtrPut bput;
120122
bput.name = std::move(bp.name);
121123
bput.offset = std::move(bp.param.offset);
122124
bput.extent = std::move(bp.param.extent);
123-
/*
124-
* Note: Moving is required here since it's a unique_ptr.
125-
* std::forward<>() would theoretically work, but it
126-
* requires the type parameter and we don't have that
127-
* inside the lambda.
128-
* (ptr_type does not work for this case).
129-
*/
130-
// clang-format off
131-
bput.data = std::move(arg); // NOLINT(bugprone-move-forwarding-reference)
132-
// clang-format on
125+
bput.data = arg.release();
133126
bput.dtype = bp.param.dtype;
134127
ba.m_uniquePtrPuts.push_back(std::move(bput));
135128
}
@@ -139,7 +132,7 @@ void WriteDataset::call(ADIOS2File &ba, detail::BufferedPut &bp)
139132
always_false_v<ptr_type>, "Unhandled std::variant branch");
140133
}
141134
},
142-
bp.param.data.m_buffer);
135+
bp.param.data.as_variant<auxiliary::WriteBufferTypes>());
143136
}
144137

145138
template <int n, typename... Params>

src/auxiliary/Memory.cpp

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@
2020
*/
2121

2222
#include "openPMD/auxiliary/Memory.hpp"
23+
#include "openPMD/ChunkInfo.hpp"
24+
#include "openPMD/auxiliary/Memory_internal.hpp"
25+
#include "openPMD/auxiliary/UniquePtr.hpp"
2326

27+
#include <any>
2428
#include <complex>
2529
#include <functional>
2630
#include <iostream>
@@ -157,24 +161,58 @@ allocatePtr(Datatype dtype, Extent const &e)
157161
return allocatePtr(dtype, numPoints);
158162
}
159163

160-
WriteBuffer::WriteBuffer() : m_buffer(UniquePtrWithLambda<void>())
164+
WriteBuffer::CopyableUniquePtr::CopyableUniquePtr() = default;
165+
166+
WriteBuffer::CopyableUniquePtr::CopyableUniquePtr(
167+
UniquePtrWithLambda<void> ptr_in)
168+
: parent_t{std::make_shared<UniquePtrWithLambda<void>>(std::move(ptr_in))}
161169
{}
162170

163-
WriteBuffer::WriteBuffer(WriteBuffer &&) noexcept(
164-
noexcept(EligibleTypes(std::declval<EligibleTypes &&>()))) = default;
165-
WriteBuffer &WriteBuffer::operator=(WriteBuffer &&) noexcept(noexcept(
166-
std::declval<EligibleTypes &>() = std::declval<EligibleTypes &&>())) =
167-
default;
171+
auto WriteBuffer::CopyableUniquePtr::get() -> void *
172+
{
173+
return (**this).get();
174+
}
175+
176+
auto WriteBuffer::CopyableUniquePtr::get() const -> void const *
177+
{
178+
return (**this).get();
179+
}
180+
181+
auto WriteBuffer::CopyableUniquePtr::release() -> UniquePtrWithLambda<void>
182+
{
183+
if (parent_t::use_count() > 1)
184+
{
185+
throw error::Internal(
186+
"Control flow error: UniquePtr variant of WriteBuffer "
187+
"has been copied.");
188+
}
189+
UniquePtrWithLambda<void> res = std::move(**this);
190+
this->reset();
191+
return res;
192+
}
193+
194+
WriteBuffer::WriteBuffer() : m_buffer(std::make_any<CopyableUniquePtr>())
195+
{}
196+
WriteBuffer::WriteBuffer(std::shared_ptr<void const> ptr)
197+
: m_buffer(std::make_any<WriteBufferTypes>(std::move(ptr)))
198+
{}
199+
WriteBuffer::WriteBuffer(UniquePtrWithLambda<void> ptr)
200+
: m_buffer(
201+
std::make_any<WriteBufferTypes>(CopyableUniquePtr(std::move(ptr))))
202+
{}
203+
204+
WriteBuffer::WriteBuffer(WriteBuffer &&) noexcept = default;
205+
WriteBuffer &WriteBuffer::operator=(WriteBuffer &&) noexcept = default;
168206

169207
WriteBuffer const &WriteBuffer::operator=(std::shared_ptr<void const> ptr)
170208
{
171-
m_buffer = std::move(ptr);
209+
m_buffer = std::make_any<WriteBufferTypes>(std::move(ptr));
172210
return *this;
173211
}
174-
175-
WriteBuffer const &WriteBuffer::operator=(UniquePtrWithLambda<void const> ptr)
212+
WriteBuffer const &WriteBuffer::operator=(UniquePtrWithLambda<void> ptr)
176213
{
177-
m_buffer = std::move(ptr);
214+
m_buffer =
215+
std::make_any<WriteBufferTypes>(CopyableUniquePtr(std::move(ptr)));
178216
return *this;
179217
}
180218

@@ -186,6 +224,6 @@ void const *WriteBuffer::get() const
186224
// we're being sneaky and don't distinguish the types here
187225
return static_cast<void const *>(arg.get());
188226
},
189-
m_buffer);
227+
as_variant<WriteBufferTypes>());
190228
}
191229
} // namespace openPMD::auxiliary

test/CoreTest.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
#include "openPMD/IO/ADIOS/macros.hpp"
1313
#include "openPMD/auxiliary/Filesystem.hpp"
14-
#include "openPMD/auxiliary/JSON.hpp"
14+
#include "openPMD/auxiliary/Memory_internal.hpp"
1515
#include "openPMD/auxiliary/UniquePtr.hpp"
1616

1717
#include <catch2/catch.hpp>
@@ -1252,7 +1252,7 @@ TEST_CASE("use_count_test", "[core]")
12521252
std::get<std::shared_ptr<void const>>(
12531253
static_cast<Parameter<Operation::WRITE_DATASET> *>(
12541254
pprc.get().m_chunks.front().parameter.get())
1255-
->data.m_buffer)
1255+
->data.as_variant<auxiliary::WriteBufferTypes>())
12561256
.use_count() == 1);
12571257
#endif
12581258
}

0 commit comments

Comments
 (0)