diff --git a/include/utl/init_from.h b/include/utl/init_from.h new file mode 100644 index 0000000..e00bcbf --- /dev/null +++ b/include/utl/init_from.h @@ -0,0 +1,154 @@ +#pragma once + +#include +#include +#include + +namespace utl { + +namespace detail { + +template +using field_count = std::integral_constant; + +struct wildcard { + template ::value>> + operator T&&() const; + + template ::value>> + operator T&() const; +}; + +template +static constexpr const wildcard _{}; + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + +template +inline constexpr auto is_brace_constructible_(std::index_sequence, T*) + -> decltype(T{_...}, std::true_type{}) { + return {}; +} + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +template +inline constexpr std::false_type is_brace_constructible_( + std::index_sequence, ...) { + return {}; +} + +template +constexpr auto is_brace_constructible() + -> decltype(is_brace_constructible_(std::make_index_sequence{}, + static_cast(nullptr))) { + return {}; +} + +template +struct is_paren_constructible_; + +template +struct is_paren_constructible_> + : std::is_constructible)...> {}; + +template +constexpr auto is_paren_constructible() + -> is_paren_constructible_> { + return {}; +} + +template +inline constexpr auto const has_arity_v = + is_brace_constructible() && !is_brace_constructible(); + +#define CISTA_MAKE_ARITY_FUNC(count) \ + template >> \ + constexpr field_count arity() { \ + return {}; \ + } + +CISTA_MAKE_ARITY_FUNC(0) +CISTA_MAKE_ARITY_FUNC(1) +CISTA_MAKE_ARITY_FUNC(2) +CISTA_MAKE_ARITY_FUNC(3) + +#undef CISTA_MAKE_ARITY_FUNC + +template +auto to_tuple(T& t) { + constexpr auto const a = arity(); + static_assert(a <= 3U, "Max. supported members: 3"); + if constexpr (a == 0U) { + return std::tie(); + } else if constexpr (a == 1U) { + auto& [p1] = t; + return std::tie(p1); + } else if constexpr (a == 2U) { + auto& [p1, p2] = t; + return std::tie(p1, p2); + } else if constexpr (a == 3U) { + auto& [p1, p2, p3] = t; + return std::tie(p1, p2, p3); + } +} + +template +consteval T* null() noexcept { + return static_cast(nullptr); +} + +template +concept Derefable = requires(T t) { *t; }; + +template +struct derefable { + static constexpr bool const value = Derefable; +}; + +template +using deref_t = decltype(*std::declval()); + +template +struct deref_same { + static constexpr auto const value = + std::is_same_v>>; +}; + +template +T& m(S&& s) { + using Src = std::decay_t>>; + using Target = std::decay_t; + + if constexpr (std::is_same_v) { + return std::get(s); + } else if constexpr (std::conjunction_v, + deref_same>) { + return *std::get(s); + } else { + return m(std::forward(s)); + } +} + +template +T init_from(S&& s, std::index_sequence) { + using Tuple = decltype(to_tuple(std::declval())); + return T(m>(s)...); +} + +} // namespace detail + +template +T init_from(S&& s) { + return detail::init_from(detail::to_tuple(s), + std::make_index_sequence()>()); +} + +} // namespace utl diff --git a/include/utl/pipes/take_while.h b/include/utl/pipes/take_while.h index 17eb877..ca02a27 100644 --- a/include/utl/pipes/take_while.h +++ b/include/utl/pipes/take_while.h @@ -11,7 +11,8 @@ template struct take_while_range : public clear_t { using parent_t = clear_t; - take_while_range(Range&& r, TakeWhile&& take_while) + template + take_while_range(R&& r, TakeWhile&& take_while) : parent_t(std::forward(r)), take_while_(std::forward(take_while)) {} diff --git a/test/init_from_test.cc b/test/init_from_test.cc new file mode 100644 index 0000000..93b1a8d --- /dev/null +++ b/test/init_from_test.cc @@ -0,0 +1,31 @@ +#include "gtest/gtest.h" + +#include "utl/init_from.h" + +TEST(init_from, init_from) { + struct a { + int& i_; + float& j_; + int& k_; + }; + + struct b { + double z_; + std::unique_ptr x_; + int y_; + }; + + auto src = b{0.0, std::make_unique(10.0F), 42}; + auto tgt = utl::init_from(src); + EXPECT_EQ(42, tgt.i_); + EXPECT_EQ(10.0F, tgt.j_); + EXPECT_EQ(42, tgt.k_); + + tgt.i_ = 7.0F; + tgt.j_ = 77.0F; + EXPECT_EQ(77.0F, *src.x_); + EXPECT_EQ(7.0F, src.y_); + + tgt.k_ = 99; + EXPECT_EQ(99, src.y_); +}