4
\$\begingroup\$

C++17 introduces std::invoke which calls the exposition-only INVOKE. Here is an implementation. This isn't a large amount of code, and it is to a large extent a transliteration of [func.require].

I took the liberty to add invoke_r which calls INVOKE<R> since I find it quite useful at times.

// C++17 std::invoke implementation

#ifndef INC_INVOKE_HPP_6AY08xPaOo
#define INC_INVOKE_HPP_6AY08xPaOo

#include <functional>
#include <type_traits>
#include <utility>

namespace my_std {

  // 23.14.4 Function template invoke [func.invoke]
  // Returns: INVOKE(std​::​forward<F>(f), std​::​forward<Args>(args)...)
  // (See 23.14.3 Requirements [func.require] Paragraph 1)
  template <typename F, typename... Args>
  std::invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)
    noexcept(std::is_nothrow_invocable_v<F, Args...>);

  // Added functionality
  // Returns: INVOKE<R>(std::forward<F>(f), std::forward<Args>(args)...)
  // (See 23.14.3 Requirements [func.require] Paragraph 2)
  template <typename R, typename F, typename... Args>
  R invoke_r(F&& f, Args&&... args)
    noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>);

  namespace detail {
    template <typename T>
    struct is_reference_wrapper :std::false_type {};
    template <typename T>
    struct is_reference_wrapper<std::reference_wrapper<T>>
      :std::true_type {};
    template <typename T>
    inline constexpr bool is_reference_wrapper_v =
      is_reference_wrapper<T>::value;

    template <typename F, typename C, typename T, typename... Args>
    decltype(auto) invoke_mfp(F C::*f, T&& t, Args&&... args)
    {
      // (1.1)
      if constexpr (std::is_base_of_v<C, std::decay_t<T>>)
        return (t.*f)(std::forward<Args>(args)...);
      // (1.2)
      else if constexpr (is_reference_wrapper_v<std::decay_t<T>>)
        return (t.get().*f)(std::forward<Args>(args)...);
      // (1.3)
      else
        return ((*t).*f)(std::forward<Args>(args)...);
    }

    template <typename M, typename C, typename T>
    decltype(auto) invoke_mop(M C::*f, T&& t)
    {
      // (1.4)
      if constexpr (std::is_base_of_v<C, std::decay_t<T>>)
        return t.*f;
      // (1.5)
      else if constexpr (is_reference_wrapper_v<std::decay_t<T>>)
        return t.get().*f;
      // (1.6)
      else
        return (*t).*f;
    }
  }

  template <typename F, typename... Args>
  std::invoke_result_t<F, Args...> invoke(F&& f, Args&&... args)
    noexcept(std::is_nothrow_invocable_v<F, Args...>)
  {
    // (1.1), (1.2), (1.3)
    if constexpr (std::is_member_function_pointer_v<std::remove_reference_t<F>>)
      return detail::invoke_mfp(f, std::forward<Args>(args)...);
    // (1.4), (1.5), (1.6)
    else if constexpr (sizeof...(Args) == 1 &&
        std::is_member_object_pointer_v<std::remove_reference_t<F>>)
      return detail::invoke_mop(f, std::forward<Args>(args)...);
    // (1.7)
    else
      return std::forward<F>(f)(std::forward<Args>(args)...);
  }

  template <typename R, typename F, typename... Args>
  R invoke_r(F&& f, Args&&... args)
    noexcept(std::is_nothrow_invocable_r_v<R, F, Args...>)
  {
    if constexpr (std::is_void_v<R>)
      return static_cast<void>(
        invoke(std::forward<F>(f), std::forward<Args>(args)...)
      );
    else
      return invoke(std::forward<F>(f), std::forward<Args>(args)...);
  }

}

#endif
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.