Skip to content

Conversation

nytelytee
Copy link
Contributor

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 to static_cast<Z>(static_cast<Y>(static_cast<X>(obj))).

Additionally, rarely useful, but geode::cast::chain_cast<>(obj) just returns obj.

@nytelytee
Copy link
Contributor Author

nytelytee commented Sep 11, 2025

Perhaps the second overload (the empty case, chain_cast<>(something)) should be removed? It is the only overload that doesn't act in the exact same way as applying static_cast repeatedly (ideally there is nothing to apply there, so it should always act literally as if the function call just doesn't exist, but I don't think it is possible to do that). It calls more constructors compared to just passing a value directly without any casts (which is what it's meant to symbolize). It also technically returns a different type.

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.

@nytelytee
Copy link
Contributor Author

nytelytee commented Sep 11, 2025

I also wrote a modify_cast, which is a concrete solution for the original problem that made chain_cast come up, namely casting a child of Base into a Modify<Derived, Base> class.

The example for chain_cast mentions a case like this:
chain_cast<GJBaseGameLayer*, MyGJBGL*>(PlayLayer::get());

The same could be done with modify_cast like so:
modify_cast<MyGJBGL*>(PlayLayer::get());

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 m_fields attribute, since that allowed me to specialize on FieldIntermediate. It also only works on pointers, and explicitly checks for pointers; I am not sure if anyone would ever be working with anything other than pointers when it comes to nodes, so...

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant