Search code examples
c++c++11std-function

Emulate std::function functionality


I'm trying to emulate the std::function functionality below, but I'm running into the following error

class Player
{
public:
void move_to(Point location);
};
std::function<void(Player&, Point)> fun = &Player::move_to;
Player hero;
fun(hero, point{ 2, 4 });
fp.cc:32:33: error: variable ‘fun<void(Player&, Point)> f’ has initializer but incomplete type
   32 |    fun<void(Player &, Point p)> f = &Player::move_to;

#include <iostream>

using namespace std;
template <typename T>
struct fun;

template <typename Ret, typename T, typename ...Args>
struct fun <Ret(*)(T&, Args...)>{
   char *data;
   using fptr = Ret(T::*)(Args...);
   fun(fptr p) : data(p) {}
   Ret operator()(T &t, Args... args) {
      if (std::is_same_v<Ret, void>) {
         (t.*((fptr)data))(args...);
      }
      return (t.*((fptr)data))(args...); 
   }
};

struct Point {
   int x;
   int y;
};

struct Player {
   void move_to(Point p) {
      cout << __PRETTY_FUNCTION__ << endl;
   }
};

int main() {
   fun<void(Player &, Point p)> f = &Player::move_to;
   Point p{1,2};
   Player pl;
//   f(pl, p);
}

Solution

  • struct unused_t {};
    // can store any of a function pointer, member function pointer, or other
    // pointer.  It is a bit annoying, and not complete even.
    union state_t {
      void(unused_t::*pm)();
      void(*pf)();
      void*pv;
      state_t():pv(nullptr){}
      template<class R0, class...A0s>
      state_t(R0(*f)(A0s...)):pf(reinterpret_cast<void(*)()>(f)) {}
      template<class X, class R0, class...A0s>
      state_t(R0(X::*f)(A0s...)):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
      template<class X, class R0, class...A0s>
      state_t(R0(X::*f)(A0s...) const):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
      // add in & and && overloads of member function pointers here
    
      template<class T>
      state_t(T* p):pv((void*)p) {}
    };
    template<class Sig>
    struct fun;
    
    template <class R, class...As>
    struct fun <R(As...)>{
      using pf_t = R(*)(state_t, As&&...);
    
      state_t state;
      pf_t pf = nullptr;
    
      fun() = default;
      explicit operator bool() const { return pf; }
      template<class R0, class...A0s>
      fun( R0(*f)(A0s...) ):
        state(f),
        pf([](state_t state, As&&...as)->R{
          auto f = (R0(*)(A0s...))(state.pf);
          return std::invoke( f, std::forward<As>(as)... );
        })
      {}
      template<class T, class R0, class...A0s>
      fun( R0(T::*f)(A0s...) ):
        state(f),
        pf([](state_t state, As&&...as)->R{
          auto f = (R0(T::*)(A0s...))(state.pm);
          return std::invoke( f, std::forward<As>(as)... );
        })
      {}
      template<class T, class R0, class...A0s>
      fun( R0(T::*f)(A0s...) const ):
        state(f),
        pf([](state_t state, As&&...as)->R{
          auto f = (R0(T::*)(A0s...) const)(state.pm);
          return std::invoke( f, std::forward<As>(as)... );
        })
      {}
      R operator()(As... args) const {
        return pf( state, std::forward<As>(args)... );
      }
    };
    

    this does use std::invoke.

    Test code:

    struct bob {
      int foo() const { return 3; };
    };
    int main() {
      fun<int(bob&)> f = &bob::foo;
      bob b;
      std::cout << f(b);
    }
    

    Live example.

    Here is a version:

    struct unused_t {};
    // can store any of a function pointer, member function pointer, or other
    // pointer.  It is a bit annoying, and not complete even.
    union state_t {
      void(unused_t::*pm)();
      void(*pf)();
      void*pv;
      state_t():pv(nullptr){}
      template<class R0, class...A0s>
      state_t(R0(*f)(A0s...)):pf(reinterpret_cast<void(*)()>(f)) {}
      template<class X, class R0, class...A0s>
      state_t(R0(X::*f)(A0s...)):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
      template<class X, class R0, class...A0s>
      state_t(R0(X::*f)(A0s...) const):pm(reinterpret_cast<void(unused_t::*)()>(f)) {}
      // add in & and && overloads of member function pointers here
    
      template<class T>struct tag_t {};
      template<class T>using type_t = T;
    
      template<class R, class...As>
      type_t<R(*)(As...)> extract( tag_t<R(*)(As...)>) const {
        return (R(*)(As...))pf;
      }
      template<class T, class R, class...As>
      type_t<R(T::*)(As...)> extract( tag_t<R(T::*)(As...)>) const {
        return (R(T::*)(As...))pm;
      }
      template<class T, class R, class...As>
      type_t<R(T::*)(As...) const> extract( tag_t<R(T::*)(As...) const>) const {
        return (R(T::*)(As...) const)pm;
      }
      template<class T>
      type_t<T*> extract( tag_t<T*>) const {
        return (T*)pv;
      }
      template<class T>
      state_t(T* p):pv((void*)p) {}
    };
    template<class Sig>
    struct fun;
    
    template <class R, class...As>
    struct fun <R(As...)>{
      using pf_t = R(*)(state_t, As&&...);
    
      state_t state;
      pf_t pf = nullptr;
    
      fun() = default;
      explicit operator bool() const { return pf; }
      template<std::invocable<As...> F>
      requires std::is_convertible_v< std::invoke_result_t<F, As...>, R >
      fun( F f ):
        state(std::forward<F>(f)),
        pf([](state_t state, As&&...as)->R{
          auto f = state.extract(state_t::tag_t<F>{});
          return std::invoke( f, std::forward<As>(as)... );
        })
      {}
      R operator()(As... args) const {
        return pf( state, std::forward<As>(args)... );
      }
    };
    

    the annoying part is the storage of a pointer to member function, pointer or function pointer in a uniform way. The C++ standard states that those 3 types of pointers are not guaranteed compatible.

    So the state_t type can store any one of the 3 in a union, but there is a bunch of boilerplate to treat them uniformly.