-
Couldn't load subscription status.
- Fork 196
Add modify_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. |
|
modify_cast is fine, but id say instead of chain_cast just use dynamic cast/typeinfo cast instead cause thats its point, can merge if you implement it based on that, also using PascalCase for every type, modify_cast is fine though, and make sure that the namespace name is fitting, by going through other internal namespaces in headers |
|
I removed Sidenote, I think that even if you could always correctly determine which cast to use at compile time just based on the types alone, I think that it would be unintuitive, as people would have to track whether their All of this said, I haven't made 2 separate versions because I don't know if it would be approved or not, so I just kept it at the original implementation for now. |
Allows you to repeatedly apply
static_castin 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.