-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
77aac49
commit 5533ed5
Showing
3 changed files
with
209 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
#include <optional> | ||
#include <tuple> | ||
#include <type_traits> | ||
#include <utility> | ||
|
||
namespace utl { | ||
|
||
namespace detail { | ||
|
||
template <std::size_t N> | ||
using field_count = std::integral_constant<std::size_t, N>; | ||
|
||
struct wildcard { | ||
template <typename T, | ||
typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>> | ||
operator T&&() const; | ||
|
||
template <typename T, | ||
typename = std::enable_if_t<std::is_copy_constructible<T>::value>> | ||
operator T&() const; | ||
}; | ||
|
||
template <std::size_t N = 0> | ||
static constexpr const wildcard _{}; | ||
|
||
#ifdef __GNUC__ | ||
#pragma GCC diagnostic push | ||
#pragma GCC diagnostic ignored "-Wmissing-field-initializers" | ||
#endif | ||
|
||
template <typename T, std::size_t... I> | ||
inline constexpr auto is_brace_constructible_(std::index_sequence<I...>, T*) | ||
-> decltype(T{_<I>...}, std::true_type{}) { | ||
return {}; | ||
} | ||
|
||
#ifdef __GNUC__ | ||
#pragma GCC diagnostic pop | ||
#endif | ||
|
||
template <std::size_t... I> | ||
inline constexpr std::false_type is_brace_constructible_( | ||
std::index_sequence<I...>, ...) { | ||
return {}; | ||
} | ||
|
||
template <typename T, std::size_t N> | ||
constexpr auto is_brace_constructible() | ||
-> decltype(is_brace_constructible_(std::make_index_sequence<N>{}, | ||
static_cast<T*>(nullptr))) { | ||
return {}; | ||
} | ||
|
||
template <typename T, typename U> | ||
struct is_paren_constructible_; | ||
|
||
template <typename T, std::size_t... I> | ||
struct is_paren_constructible_<T, std::index_sequence<I...>> | ||
: std::is_constructible<T, decltype(_<I>)...> {}; | ||
|
||
template <typename T, std::size_t N> | ||
constexpr auto is_paren_constructible() | ||
-> is_paren_constructible_<T, std::make_index_sequence<N>> { | ||
return {}; | ||
} | ||
|
||
template <typename T, std::size_t N> | ||
inline constexpr auto const has_arity_v = | ||
is_brace_constructible<T, N>() && !is_brace_constructible<T, N + 1>(); | ||
|
||
#define CISTA_MAKE_ARITY_FUNC(count) \ | ||
template <typename T, typename = std::enable_if_t<has_arity_v<T, count>>> \ | ||
constexpr field_count<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 <typename T> | ||
auto to_tuple(T& t) { | ||
constexpr auto const a = arity<T>(); | ||
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 <typename T> | ||
consteval T* null() noexcept { | ||
return static_cast<T*>(nullptr); | ||
} | ||
|
||
template <typename T> | ||
concept Derefable = requires(T t) { *t; }; | ||
|
||
template <typename T> | ||
struct derefable { | ||
static constexpr bool const value = Derefable<T>; | ||
}; | ||
|
||
template <typename T> | ||
using deref_t = decltype(*std::declval<T>()); | ||
|
||
template <typename T, typename U> | ||
struct deref_same { | ||
static constexpr auto const value = | ||
std::is_same_v<T, std::decay_t<deref_t<U>>>; | ||
}; | ||
|
||
template <typename T, typename S, std::size_t I = 0U> | ||
bool c(S&& s) { | ||
using Src = std::decay_t<std::tuple_element_t<I, std::decay_t<S>>>; | ||
using Target = std::decay_t<T>; | ||
|
||
if constexpr (std::is_same_v<Target, Src>) { | ||
return true; | ||
} else if constexpr (std::conjunction_v<derefable<Src>, | ||
deref_same<Target, Src>>) { | ||
return std::get<I>(s) != nullptr; | ||
} else { | ||
return c<T, S, I + 1U>(std::forward<S>(s)); | ||
} | ||
} | ||
|
||
template <typename T, typename S, std::size_t I = 0U> | ||
T& m(S&& s) { | ||
using Src = std::decay_t<std::tuple_element_t<I, std::decay_t<S>>>; | ||
using Target = std::decay_t<T>; | ||
|
||
if constexpr (std::is_same_v<Target, Src>) { | ||
return std::get<I>(s); | ||
} else if constexpr (std::conjunction_v<derefable<Src>, | ||
deref_same<Target, Src>>) { | ||
return *std::get<I>(s); | ||
} else { | ||
return m<T, S, I + 1U>(std::forward<S>(s)); | ||
} | ||
} | ||
|
||
template <typename T, typename S, std::size_t... I> | ||
std::optional<T> init_from(S&& s, std::index_sequence<I...>) { | ||
using Tuple = decltype(to_tuple(std::declval<T&>())); | ||
if (!(c<std::tuple_element_t<I, Tuple>>(s) && ...)) { | ||
return std::nullopt; | ||
} | ||
return T(m<std::tuple_element_t<I, Tuple>>(s)...); | ||
} | ||
|
||
} // namespace detail | ||
|
||
template <typename T, typename S> | ||
std::optional<T> init_from(S&& s) { | ||
return detail::init_from<T>(detail::to_tuple(s), | ||
std::make_index_sequence<detail::arity<T>()>()); | ||
} | ||
|
||
} // namespace utl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#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<float> x_; | ||
int y_; | ||
}; | ||
|
||
auto src = b{0.0, std::make_unique<float>(10.0F), 42}; | ||
auto tgt = utl::init_from<a>(src); | ||
ASSERT_TRUE(tgt.has_value()); | ||
|
||
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_); | ||
|
||
auto no = utl::init_from<a>(b{}); | ||
EXPECT_FALSE(no.has_value()); | ||
}; |