-
Notifications
You must be signed in to change notification settings - Fork 177
Add chain_cast into geode::cast #1485
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Perhaps the second overload (the empty case, I only added it so that if someone is doing template metaprogramming, and they try to expand a pack into a chain_cast for whatever reason (this is literally never going to happen), it would still work even if the pack is empty, but the unexpected calls on extra constructors compared to just passing the value directly could be unintuitive. Given all of this, I will just remove it. |
I also wrote a The example for The same could be done with The implementation is a bit hacky, I was not sure how I would fetch the base class from a modify class; I ended up using the Implementation is as follows: namespace internal {
// extract the base from the modify class, by checking its m_fields member;
// the m_fields member is a FieldIntermediate<Derived, Base>, which gives us access to
// Base when specialized; this is the only way i could figure out to extract the base
// it looks pretty hacky to me, but i don't know a better way
// type will also be void if there is no m_fields, or m_fields is not FieldIntermediate<Derived, Base>
template<typename T, typename = void>
struct extract_modify_base { using type = void; };
template<typename T>
struct extract_modify_base<T, std::void_t<decltype(T::m_fields)>> {
private:
template<typename U>
struct extract_base { using type = void; };
template<typename Derived, typename Base>
struct extract_base<FieldIntermediate<Derived, Base>> { using type = Base; };
public:
using type = typename extract_base<decltype(T::m_fields)>::type;
};
template<typename T>
using extract_modify_base_t = typename extract_modify_base<T>::type;
}
template<typename Target, typename Original>
constexpr Target modify_cast(Original original) {
using TargetBase = ::internal::extract_modify_base_t<std::remove_pointer_t<Target>>;
static_assert(std::is_pointer_v<Target> && !std::is_pointer_v<std::remove_pointer_t<Target>>, "Target class has to be a single pointer.");
static_assert(std::is_pointer_v<Original> && !std::is_pointer_v<std::remove_pointer_t<Original>>, "Original class has to be a single pointer.");
static_assert(
(
requires { std::remove_pointer_t<Target>::m_fields; !std::is_void_v<TargetBase>; } &&
// satisfies the above, but does not inherit from Modify<TargetBase, std::remove_pointer_t<Target>>
// someone tried to spoof a modify class???? why would you do that
std::is_base_of_v<geode::Modify<std::remove_pointer_t<Target>, TargetBase>, std::remove_pointer_t<Target>>
),
"The target class has to be a Modify class."
);
static_assert(
!std::is_void_v<TargetBase> && requires { static_cast<TargetBase*>(original); },
"The original class has to be castable to the class the modify class is modifying."
);
return static_cast<Target>(static_cast<TargetBase*>(original));
}
I am not going to add it to this PR until someone notifies me whether it's OK or not, since it may be out of scope for the PR. |
Allows you to repeatedly apply
static_cast
in the order the casts are applied in.So,
geode::cast::chain_cast<X, Y, Z>(obj)
is equivalent tostatic_cast<Z>(static_cast<Y>(static_cast<X>(obj)))
.Additionally, rarely useful, but
geode::cast::chain_cast<>(obj)
just returns obj.