diff --git a/include/openPMD/auxiliary/UniquePtr.hpp b/include/openPMD/auxiliary/UniquePtr.hpp index 4b99f36790..6a64763b13 100644 --- a/include/openPMD/auxiliary/UniquePtr.hpp +++ b/include/openPMD/auxiliary/UniquePtr.hpp @@ -30,24 +30,14 @@ namespace auxiliary public: using deleter_type = std::function; - deleter_type const &get_deleter() const - { - return *this; - } - deleter_type &get_deleter() - { - return *this; - } - /* * Default constructor: Use std::default_delete. * This ensures correct destruction of arrays by using delete[]. */ CustomDelete() - : deleter_type{[](T_decayed *ptr) { + : deleter_type{[]([[maybe_unused]] T_decayed *ptr) { if constexpr (std::is_void_v) { - (void)ptr; std::cerr << "[Warning] Cannot standard-delete a void-type " "pointer. Please specify a custom destructor. " "Will let the memory leak." @@ -144,12 +134,25 @@ UniquePtrWithLambda::UniquePtrWithLambda(std::unique_ptr stdPtr) template template UniquePtrWithLambda::UniquePtrWithLambda(std::unique_ptr ptr) - : BasePtr{ - ptr.release(), - auxiliary::CustomDelete{ - [deleter = std::move(ptr.get_deleter())](T_decayed *del_ptr) { - deleter.get_deleter()(del_ptr); - }}} + : BasePtr{ptr.release(), auxiliary::CustomDelete{[&]() { + if constexpr (std::is_copy_constructible_v) + { + return [deleter = std::move(ptr.get_deleter())]( + T_decayed *del_ptr) { deleter(del_ptr); }; + } + else + { + /* + * The constructor of std::function requires a copyable + * lambda. Since Del is not a copyable type, we cannot + * capture it directly, but need to put it into a + * shared_ptr to make it copyable. + */ + return [deleter = std::make_shared( + std::move(ptr.get_deleter()))]( + T_decayed *del_ptr) { (*deleter)(del_ptr); }; + } + }()}} {} template @@ -170,7 +173,7 @@ UniquePtrWithLambda UniquePtrWithLambda::static_cast_() && return UniquePtrWithLambda{ static_cast(this->release()), [deleter = std::move(this->get_deleter())](other_type *ptr) { - deleter.get_deleter()(static_cast(ptr)); + deleter(static_cast(ptr)); }}; } } // namespace openPMD diff --git a/test/SerialIOTest.cpp b/test/SerialIOTest.cpp index 962ca636aa..2ab428c83f 100644 --- a/test/SerialIOTest.cpp +++ b/test/SerialIOTest.cpp @@ -579,6 +579,29 @@ TEST_CASE("close_iteration_interleaved_test", "[serial]") } } +namespace detail +{ +template +struct CopyableDeleter : std::function +{ + CopyableDeleter() + : std::function{[](T const *ptr) { delete[] ptr; }} + {} +}; + +template +struct NonCopyableDeleter : std::function +{ + NonCopyableDeleter() + : std::function{[](T const *ptr) { delete[] ptr; }} + {} + NonCopyableDeleter(NonCopyableDeleter const &) = delete; + NonCopyableDeleter &operator=(NonCopyableDeleter const &) = delete; + NonCopyableDeleter(NonCopyableDeleter &&) = default; + NonCopyableDeleter &operator=(NonCopyableDeleter &&) = default; +}; +} // namespace detail + void close_and_copy_attributable_test(std::string file_ending) { using position_t = int; @@ -685,6 +708,27 @@ void close_and_copy_attributable_test(std::string file_ending) {0}, {global_extent}); + // UniquePtrWithLambda from unique_ptr with custom delete type + auto pos_v = electronPositions["v"]; + pos_v.resetDataset(dataset); + std::unique_ptr> + ptr_v_copyable_deleter(new int[10]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + pos_v.storeChunk( + UniquePtrWithLambda(std::move(ptr_v_copyable_deleter)), + {0}, + {global_extent}); + + // UniquePtrWithLambda from unique_ptr with non-copyable custom delete + // type + auto posOff_v = electronPositionsOffset["v"]; + posOff_v.resetDataset(dataset); + std::unique_ptr> + ptr_v_noncopyable_deleter(new int[10]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); + posOff_v.storeChunk( + UniquePtrWithLambda(std::move(ptr_v_noncopyable_deleter)), + {0}, + {global_extent}); + iteration_ptr->close(); // force re-flush of previous iterations series.flush(); @@ -940,7 +984,7 @@ inline void constant_scalar(std::string file_ending) s.iterations[1] .meshes["rho"][MeshRecordComponent::SCALAR] .getAttribute("shape") - .get >() == Extent{1, 2, 3}); + .get>() == Extent{1, 2, 3}); REQUIRE(s.iterations[1] .meshes["rho"][MeshRecordComponent::SCALAR] .containsAttribute("value")); @@ -962,7 +1006,7 @@ inline void constant_scalar(std::string file_ending) s.iterations[1] .meshes["E"]["x"] .getAttribute("shape") - .get >() == Extent{1, 2, 3}); + .get>() == Extent{1, 2, 3}); REQUIRE(s.iterations[1].meshes["E"]["x"].containsAttribute("value")); REQUIRE( s.iterations[1] @@ -990,7 +1034,7 @@ inline void constant_scalar(std::string file_ending) s.iterations[1] .particles["e"]["position"][RecordComponent::SCALAR] .getAttribute("shape") - .get >() == Extent{3, 2, 1}); + .get>() == Extent{3, 2, 1}); REQUIRE(s.iterations[1] .particles["e"]["position"][RecordComponent::SCALAR] .containsAttribute("value")); @@ -1014,7 +1058,7 @@ inline void constant_scalar(std::string file_ending) s.iterations[1] .particles["e"]["positionOffset"][RecordComponent::SCALAR] .getAttribute("shape") - .get >() == Extent{3, 2, 1}); + .get>() == Extent{3, 2, 1}); REQUIRE(s.iterations[1] .particles["e"]["positionOffset"][RecordComponent::SCALAR] .containsAttribute("value")); @@ -1036,7 +1080,7 @@ inline void constant_scalar(std::string file_ending) s.iterations[1] .particles["e"]["velocity"]["x"] .getAttribute("shape") - .get >() == Extent{3, 2, 1}); + .get>() == Extent{3, 2, 1}); REQUIRE( s.iterations[1].particles["e"]["velocity"]["x"].containsAttribute( "value")); @@ -1388,55 +1432,55 @@ inline void dtype_test(const std::string &backend) REQUIRE(s.getAttribute("emptyString").get().empty()); } REQUIRE( - s.getAttribute("vecChar").get >() == + s.getAttribute("vecChar").get>() == std::vector({'c', 'h', 'a', 'r'})); REQUIRE( - s.getAttribute("vecInt16").get >() == + s.getAttribute("vecInt16").get>() == std::vector({32766, 32767})); REQUIRE( - s.getAttribute("vecInt32").get >() == + s.getAttribute("vecInt32").get>() == std::vector({2147483646, 2147483647})); REQUIRE( - s.getAttribute("vecInt64").get >() == + s.getAttribute("vecInt64").get>() == std::vector({9223372036854775806, 9223372036854775807})); REQUIRE( - s.getAttribute("vecUchar").get >() == + s.getAttribute("vecUchar").get>() == std::vector({'u', 'c', 'h', 'a', 'r'})); REQUIRE( - s.getAttribute("vecSchar").get >() == + s.getAttribute("vecSchar").get>() == std::vector({'s', 'c', 'h', 'a', 'r'})); REQUIRE( - s.getAttribute("vecUint16").get >() == + s.getAttribute("vecUint16").get>() == std::vector({65534u, 65535u})); REQUIRE( - s.getAttribute("vecUint32").get >() == + s.getAttribute("vecUint32").get>() == std::vector({4294967294u, 4294967295u})); REQUIRE( - s.getAttribute("vecUint64").get >() == + s.getAttribute("vecUint64").get>() == std::vector({18446744073709551614u, 18446744073709551615u})); REQUIRE( - s.getAttribute("vecFloat").get >() == + s.getAttribute("vecFloat").get>() == std::vector({0.f, 3.40282e+38f})); REQUIRE( - s.getAttribute("vecDouble").get >() == + s.getAttribute("vecDouble").get>() == std::vector({0., 1.79769e+308})); if (test_long_double) { REQUIRE( - s.getAttribute("vecLongdouble").get >() == + s.getAttribute("vecLongdouble").get>() == std::vector( {0.L, std::numeric_limits::max()})); } REQUIRE( - s.getAttribute("vecString").get >() == + s.getAttribute("vecString").get>() == std::vector({"vector", "of", "strings"})); if (!adios1) { REQUIRE( - s.getAttribute("vecEmptyString").get >() == + s.getAttribute("vecEmptyString").get>() == std::vector({"", "", ""})); REQUIRE( - s.getAttribute("vecMixedString").get >() == + s.getAttribute("vecMixedString").get>() == std::vector({"hi", "", "ho"})); } REQUIRE(s.getAttribute("bool").get() == true); @@ -1648,7 +1692,7 @@ void test_complex(const std::string &backend) "longDoublesYouSay", std::complex(5.5, -4.55)); auto Cflt = o.iterations[0].meshes["Cflt"][RecordComponent::SCALAR]; - std::vector > cfloats(3); + std::vector> cfloats(3); cfloats.at(0) = {1., 2.}; cfloats.at(1) = {-3., 4.}; cfloats.at(2) = {5., -6.}; @@ -1656,14 +1700,14 @@ void test_complex(const std::string &backend) Cflt.storeChunk(cfloats, {0}); auto Cdbl = o.iterations[0].meshes["Cdbl"][RecordComponent::SCALAR]; - std::vector > cdoubles(3); + std::vector> cdoubles(3); cdoubles.at(0) = {2., 1.}; cdoubles.at(1) = {-4., 3.}; cdoubles.at(2) = {6., -5.}; Cdbl.resetDataset(Dataset(Datatype::CDOUBLE, {cdoubles.size()})); Cdbl.storeChunk(cdoubles, {0}); - std::vector > cldoubles(3); + std::vector> cldoubles(3); if (o.backend() != "ADIOS2" && o.backend() != "ADIOS1" && o.backend() != "MPI_ADIOS1") { @@ -1684,26 +1728,26 @@ void test_complex(const std::string &backend) Series i = Series( "../samples/serial_write_complex." + backend, Access::READ_ONLY); REQUIRE( - i.getAttribute("lifeIsComplex").get >() == + i.getAttribute("lifeIsComplex").get>() == std::complex(4.56, 7.89)); REQUIRE( - i.getAttribute("butComplexFloats").get >() == + i.getAttribute("butComplexFloats").get>() == std::complex(42.3, -99.3)); if (i.backend() != "ADIOS2" && i.backend() != "ADIOS1" && i.backend() != "MPI_ADIOS1") { REQUIRE( i.getAttribute("longDoublesYouSay") - .get >() == + .get>() == std::complex(5.5, -4.55)); } auto rcflt = i.iterations[0] .meshes["Cflt"][RecordComponent::SCALAR] - .loadChunk >(); + .loadChunk>(); auto rcdbl = i.iterations[0] .meshes["Cdbl"][RecordComponent::SCALAR] - .loadChunk >(); + .loadChunk>(); i.flush(); REQUIRE(rcflt.get()[1] == std::complex(-3., 4.)); @@ -1714,7 +1758,7 @@ void test_complex(const std::string &backend) { auto rcldbl = i.iterations[0] .meshes["Cldbl"][RecordComponent::SCALAR] - .loadChunk >(); + .loadChunk>(); i.flush(); REQUIRE(rcldbl.get()[2] == std::complex(7., -6.)); } @@ -4957,7 +5001,7 @@ void bp4_steps( auto E_x = E["x"]; REQUIRE( E.getAttribute("vector_of_string") - .get >() == + .get>() == std::vector{"vector", "of", "string"}); REQUIRE(E_x.getDimensionality() == 1); REQUIRE(E_x.getExtent()[0] == 10); @@ -5191,7 +5235,7 @@ struct AreEqual }; template -struct AreEqual > +struct AreEqual> { static bool areEqual(std::vector v1, std::vector v2) {