diff --git a/include/openPMD/RecordComponent.hpp b/include/openPMD/RecordComponent.hpp index 3443b27d7a..90352268d5 100644 --- a/include/openPMD/RecordComponent.hpp +++ b/include/openPMD/RecordComponent.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include // expose private and protected members for invasive testing @@ -97,6 +98,29 @@ namespace internal }; } // namespace internal +class RecordComponent; + +namespace detail +{ + template + struct VisitRecordComponent + { + template + static Res call(RecordComponent &rc, Args &&...args) + { + return Functor::template call(rc, std::forward(args)...); + } + + template + static Res call(Args &&...) + { + throw std::runtime_error( + "[RecordComponent::visit()] Unknown datatype in " + "RecordComponent"); + } + }; +} // namespace detail + class RecordComponent : public BaseRecordComponent { template @@ -404,6 +428,47 @@ class RecordComponent : public BaseRecordComponent template DynamicMemoryView storeChunk(Offset, Extent); + /** + * @brief Run a template functor on the type of the record component, + * similar to std::visit(). + * + * Note that unlike std::visit(), this template cannot "switch" over + * a single existing value, meaning that the interface needs to work + * a bit different. + * The functor is given as a struct/class with a call() operation. + * + * (Ideally, this can be harmonized by using template lambdas once we + * support C++20) + * + * @tparam Visitor A struct type that has a static template method: + * Visitor::template call(RecordComponent &, ...) + * In here, T will be instantiated with this RecordComponent's type + * and a reference to this RecordComponent will be passed as first + * argument. + * + * @tparam Args Types of optional further arguments. + * @param args Optional further arguments that will be forwarded to + * Visitor::template call() + * @return Whatever Visitor::template call() returned. + * Take special note that the return types must match (i.e. cannot + * be different across instantiations + * of T for Visitor::template call()). + * Formally, the return type is that of + * Visitor::template call(RecordComponent &, Args&&...) + * and returned values from other template instantiations might then + * be implicitly converted. + */ + template + constexpr auto visit(Args &&...args) + -> decltype(Visitor::template call( + std::declval(), std::forward(args)...)) + { + using Res = decltype(Visitor::template call( + std::declval(), std::forward(args)...)); + return switchNonVectorType>( + getDatatype(), *this, std::forward(args)...); + } + static constexpr char const *const SCALAR = "\vScalar"; private: diff --git a/test/SerialIOTest.cpp b/test/SerialIOTest.cpp index 962ca636aa..5f58a26548 100644 --- a/test/SerialIOTest.cpp +++ b/test/SerialIOTest.cpp @@ -1525,6 +1525,15 @@ TEST_CASE("dtype_test", "[serial]") } } +struct ReadFromAnyType +{ + template + static std::shared_ptr call(RecordComponent &rc) + { + return std::static_pointer_cast(rc.loadChunk()); + } +}; + inline void write_test(const std::string &backend) { Series o = Series("../samples/serial_write." + backend, Access::CREATE); @@ -1623,6 +1632,12 @@ inline void write_test(const std::string &backend) } o.flush(); + + o.close(); + + Series read("../samples/serial_write." + backend, Access::READ_ONLY); + auto rc = read.iterations[1].particles["e"]["position"]["x"]; + auto opaqueTypeDataset = rc.visit(); } TEST_CASE("write_test", "[serial]")