diff --git a/include/llama/View.hpp b/include/llama/View.hpp index cfb73f5d39..9b765f94ca 100644 --- a/include/llama/View.hpp +++ b/include/llama/View.hpp @@ -236,6 +236,67 @@ namespace llama return std::tuple_cat( asFlatTupleImpl(vd(GetDatumElementTag{}), GetDatumElementType{})...); } + + template + constexpr inline auto isTupleLike = false; + + // get(t) and std::tuple_size must be available + using std::get; // make sure a get<0>() can be found, so the compiler can compile the trait + template + constexpr inline auto + isTupleLike(std::declval())), std::tuple_size>> = true; + + template + constexpr inline auto dependentFalse = false; + + template + LLAMA_FN_HOST_ACC_INLINE void assignTuples(Tuple1&& dst, Tuple2&& src, std::index_sequence); + + template + LLAMA_FN_HOST_ACC_INLINE void assignTupleElement(T1&& dst, T2&& src) + { + if constexpr (isTupleLike && isTupleLike) + { + static_assert(std::tuple_size_v == std::tuple_size_v); + assignTuples(dst, src, std::make_index_sequence>{}); + } + else if constexpr (!isTupleLike && !isTupleLike) + std::forward(dst) = std::forward(src); + else + static_assert(dependentFalse, "Elements to assign are not tuple/tuple or non-tuple/non-tuple."); + } + + template + LLAMA_FN_HOST_ACC_INLINE void assignTuples(Tuple1&& dst, Tuple2&& src, std::index_sequence) + { + static_assert(std::tuple_size_v> == std::tuple_size_v>); + using std::get; + (assignTupleElement(get(std::forward(dst)), get(std::forward(src))), ...); + } + + template + LLAMA_FN_HOST_ACC_INLINE auto makeFromTuple(Tuple&& src, std::index_sequence) + { + using std::get; + return T{get(std::forward(src))...}; + } + + template + constexpr inline auto isDirectListInitializableImpl = false; + + template + constexpr inline auto + isDirectListInitializableImpl()...})>, Args...> = true; + + template + constexpr inline auto isDirectListInitializable = isDirectListInitializableImpl; + + template + constexpr inline auto isDirectListInitializableFromTuple = false; + + template typename Tuple, typename... Args> + constexpr inline auto + isDirectListInitializableFromTuple> = isDirectListInitializable; } // namespace internal /// Virtual data type returned by \ref View after resolving a user domain @@ -528,6 +589,68 @@ namespace llama { return operator()(DatumCoord{}); } + + template + auto loadAs() -> TupleLike + { + static_assert( + internal::isDirectListInitializableFromTuple, + "TupleLike must be constructible from as many values as this VirtualDatum recursively represents like " + "this: TupleLike{values...}"); + return internal::makeFromTuple( + asFlatTuple(), + std::make_index_sequence>{}); + } + + template + auto loadAs() const -> TupleLike + { + static_assert( + internal::isDirectListInitializableFromTuple, + "TupleLike must be constructible from as many values as this VirtualDatum recursively represents like " + "this: TupleLike{values...}"); + return internal::makeFromTuple( + asFlatTuple(), + std::make_index_sequence>{}); + } + + struct Loader + { + VirtualDatum& vd; + + template + operator T() + { + return vd.loadAs(); + } + }; + + struct LoaderConst + { + const VirtualDatum& vd; + + template + operator T() const + { + return vd.loadAs(); + } + }; + + auto load() -> Loader + { + return {*this}; + } + + auto load() const -> LoaderConst + { + return {*this}; + } + + template + void store(const TupleLike& t) + { + internal::assignTuples(asTuple(), t, std::make_index_sequence>{}); + } }; } // namespace llama diff --git a/tests/virtualdatum.cpp b/tests/virtualdatum.cpp index 460ae00195..2bfe6c0f75 100644 --- a/tests/virtualdatum.cpp +++ b/tests/virtualdatum.cpp @@ -617,3 +617,264 @@ TEST_CASE("VirtualDatum.structuredBindings") CHECK(datum(tag::Weight{}) == 60); } } + +namespace +{ + template + struct MyPos + { + T a; + T y; + }; + + template + struct MyVel + { + T x; + T y; + T z; + }; + + template + struct MyDatum + { + MyPos pos; + MyVel vel; + T weight; + }; + + template + auto get(const MyPos& p) + { + if constexpr (I == 0) + return p.a; + if constexpr (I == 1) + return p.y; + } + + template + auto get(const MyVel& p) + { + if constexpr (I == 0) + return p.x; + if constexpr (I == 1) + return p.y; + if constexpr (I == 2) + return p.z; + } + + template + auto get(const MyDatum& p) + { + if constexpr (I == 0) + return p.pos; + if constexpr (I == 1) + return p.vel; + if constexpr (I == 2) + return p.weight; + } +} // namespace + +template +struct std::tuple_size> +{ + static constexpr std::size_t value = 2; +}; + +template +struct std::tuple_size> +{ + static constexpr std::size_t value = 3; +}; + +template +struct std::tuple_size> +{ + static constexpr std::size_t value = 3; +}; + +TEST_CASE("VirtualDatum.load.value") +{ + llama::One datum; + datum = 1; + + { + MyPos pos = datum(tag::Pos{}).load(); + CHECK(pos.a == 1); + CHECK(pos.y == 1); + } + { + MyPos pos = std::as_const(datum)(tag::Pos{}).load(); + CHECK(pos.a == 1); + CHECK(pos.y == 1); + } + + { + MyDatum d = datum.load(); + CHECK(d.pos.a == 1); + CHECK(d.pos.y == 1); + CHECK(d.vel.x == 1); + CHECK(d.vel.y == 1); + CHECK(d.vel.z == 1); + CHECK(d.weight == 1); + } + { + MyDatum d = std::as_const(datum).load(); + CHECK(d.pos.a == 1); + CHECK(d.pos.y == 1); + CHECK(d.vel.x == 1); + CHECK(d.vel.y == 1); + CHECK(d.vel.z == 1); + CHECK(d.weight == 1); + } +} + +TEST_CASE("VirtualDatum.load.ref") +{ + llama::One datum; + + datum = 1; + { + MyPos pos = datum(tag::Pos{}).load(); + CHECK(pos.a == 1); + CHECK(pos.y == 1); + + pos.a = 2; + pos.y = 3; + CHECK(datum(tag::Pos{}, tag::A{}) == 2); + CHECK(datum(tag::Pos{}, tag::Y{}) == 3); + CHECK(datum(tag::Vel{}, tag::X{}) == 1); + CHECK(datum(tag::Vel{}, tag::Y{}) == 1); + CHECK(datum(tag::Vel{}, tag::Z{}) == 1); + CHECK(datum(tag::Weight{}) == 1); + } + + datum = 1; + { + MyDatum d = datum.load(); + CHECK(d.pos.a == 1); + CHECK(d.pos.y == 1); + + d.pos.a = 2; + d.pos.y = 3; + d.vel.x = 4; + d.vel.y = 5; + d.vel.z = 6; + d.weight = 7; + CHECK(datum(tag::Pos{}, tag::A{}) == 2); + CHECK(datum(tag::Pos{}, tag::Y{}) == 3); + CHECK(datum(tag::Vel{}, tag::X{}) == 4); + CHECK(datum(tag::Vel{}, tag::Y{}) == 5); + CHECK(datum(tag::Vel{}, tag::Z{}) == 6); + CHECK(datum(tag::Weight{}) == 7); + } +} + +TEST_CASE("VirtualDatum.load.constref") +{ + llama::One datum; + datum = 1; + + { + MyPos pos = datum(tag::Pos{}).load(); + CHECK(pos.a == 1); + CHECK(pos.y == 1); + } + { + MyPos pos = std::as_const(datum)(tag::Pos{}).load(); + CHECK(pos.a == 1); + CHECK(pos.y == 1); + } + { + MyDatum d = datum.load(); + CHECK(d.pos.a == 1); + CHECK(d.pos.y == 1); + } + { + MyDatum d = std::as_const(datum).load(); + CHECK(d.pos.a == 1); + CHECK(d.pos.y == 1); + } +} + +TEST_CASE("VirtualDatum.store") +{ + llama::One datum; + + datum = 1; + { + MyPos pos{2, 3}; + datum(tag::Pos{}).store(pos); + CHECK(datum(tag::Pos{}, tag::A{}) == 2); + CHECK(datum(tag::Pos{}, tag::Y{}) == 3); + CHECK(datum(tag::Vel{}, tag::X{}) == 1); + CHECK(datum(tag::Vel{}, tag::Y{}) == 1); + CHECK(datum(tag::Vel{}, tag::Z{}) == 1); + CHECK(datum(tag::Weight{}) == 1); + } + + datum = 1; + { + MyDatum d{{2, 3}, {4, 5, 6}, 7}; + datum.store(d); + CHECK(datum(tag::Pos{}, tag::A{}) == 2); + CHECK(datum(tag::Pos{}, tag::Y{}) == 3); + CHECK(datum(tag::Vel{}, tag::X{}) == 4); + CHECK(datum(tag::Vel{}, tag::Y{}) == 5); + CHECK(datum(tag::Vel{}, tag::Z{}) == 6); + CHECK(datum(tag::Weight{}) == 7); + } +} + +TEST_CASE("VirtualDatum.loadAs.value") +{ + llama::One datum; + datum = 1; + + { + auto pos = datum(tag::Pos{}).loadAs>(); + CHECK(pos.a == 1); + CHECK(pos.y == 1); + } + { + auto pos = std::as_const(datum)(tag::Pos{}).loadAs>(); + CHECK(pos.a == 1); + CHECK(pos.y == 1); + } +} + +TEST_CASE("VirtualDatum.loadAs.ref") +{ + llama::One datum; + datum = 1; + + auto pos = datum(tag::Pos{}).loadAs>(); + CHECK(pos.a == 1); + CHECK(pos.y == 1); + + pos.a = 2; + pos.y = 3; + CHECK(datum(tag::Pos{}, tag::A{}) == 2); + CHECK(datum(tag::Pos{}, tag::Y{}) == 3); + CHECK(datum(tag::Vel{}, tag::X{}) == 1); + CHECK(datum(tag::Vel{}, tag::Y{}) == 1); + CHECK(datum(tag::Vel{}, tag::Z{}) == 1); + CHECK(datum(tag::Weight{}) == 1); +} + +TEST_CASE("VirtualDatum.loadAs.constref") +{ + llama::One datum; + datum = 1; + + { + auto pos = datum(tag::Pos{}).loadAs>(); + CHECK(pos.a == 1); + CHECK(pos.y == 1); + } + { + auto pos = std::as_const(datum)(tag::Pos{}).loadAs>(); + CHECK(pos.a == 1); + CHECK(pos.y == 1); + } +}