0

Question: I have a template function that takes an object pointer of the template param class, and a method pointer to a method of that class. I can then call that method on that object immediately. But I don't want to call it immediately. Instead, I want to save both pointers for future use, and call them at a later time in code that won't be contextually aware of what that type is.

In legacy C/C++99 code, we pass in a function pointer and a void* user data pointer to code that will do a callback (e.g., on a timer finishing, a user event, etc.) We'd almost invariably pass in an object pointer as the user-data, and write a one-line C function that casts the user data pointer to that type and called a method on the object:

void TimerCB( void* pvUserData, void* pvCallerData ) {
    ( (Foo*) pvUserData )->TimerDone( pvCallerData );
}

In C++11, std::function lets us pass in lambdas and std::bind, or a C function without a user data.

However, in practice, nearly every time I simply want to have a method on the current object called. I can do that with a lambda or bind but it's verbose:

  class Timer {

      :

      virtual void SubscribeTimer( const char* pszTime,
                                   std::function<void(Data*)> pfn );
  };

  void Timer::SubscribeTimer( const char* pszTime,
                             std::function<void(Data*)> pfn ) {
      cout << "  calling std::function\n";
      Data d;
      pfn( &d );
  }

  // Inside methods of class Foo:

  SubscribeTimer( "14:59:50", std::bind( &Foo::TimerDone, this, std::placeholders::_1 ) );

  SubscribeTimer( "14:59:50", [this](Data* pdata){this->TimerDone( pdata );} );

I'm able to pass in method pointers, if I know the class of their object at compile time, like this:

  class Timer {

      :

      virtual void SubscribeTimer( const char* pszTime,
                                   void (Foo::*pfn)( Data* pd ), Foo* pfoo );
  };

  void Timer::SubscribeTimer( const char* pszTime, void (Foo::*pfn)( Data* pd ), Foo* pfoo ) {
      cout << "  calling method\n";
      Data d;
      (pfoo->*pfn)( &d );
 }

  // Inside methods of class Foo:

  SubscribeTimer( "14:59:50", &Foo::TimerDone, this );

However, this is not acceptable, because my Timer class is of the utility library level of the project, and shouldn't need to be made aware of every possible user class like Foo.

OK, so it turns out I can templatize that method so I no longer need to know what the type of object that Foo is or that the method is a method of. This compiles without error. (Method and class pointer swapped so it's clear which overloaded function is called.)

  class Timer {

      :

      template<typename T> void SubscribeTimer( const char* pszTime, T* pthis,
                                                void (T::*pfn)( Data* pd ) );
  };



template<typename T> void Foo::SubscribeTimer( const char* pszTime, T* pthis,
                                                void (T::*pmethod)( Data* pd ) ) {
  cout << "  calling any method\n";
  Data d;
  (pthis->*pmethod)( &d ); // <-- PROBLEMATIC LINE
}

// Inside methods of class Foo:

SubscribeTimer( "14:59:50", this, &Foo::TimerDone );

So... Victory! That's the simpler syntax I wanted instead of the messier lambda and std::bind shown above.

BUT HERE IS MY QUESTION. The above example works because the line labeled PROBLEMATIC LINE is in a context where the compiler knows the type of pthis. But in practice, SubscribeTimer() doesn't call that callback right away. Instead, it saves that value for future reference. Long in the future, if the app is still running at 14:59:50, that callback will be called.

Milan
  • 1,743
  • 2
  • 13
  • 36
Swiss Frank
  • 1,985
  • 15
  • 33

1 Answers1

1

You already know about all the pieces of the answer (std::function, lambdas); you just need to put them together.

std::function<void(Data*)> f = [=](Data* d) { (pthis->*pmethod)(d); }

Now save this function, e.g. in a data member. When it comes time to call it, that's just

Data d;
f_member(&d);
Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85
  • OK, so rather than my client software writing the finicky lambda themselves, they just provide me the object and method, and my template converts it into a lambda which I process as if they gave me a lambda. Obvious now that you point it out! I don't know why I didn't figure that out. – Swiss Frank Jul 20 '20 at 13:07
  • OK, I want to constrain the type of T to be an object of type Xyz or subclass. I want subclasses to be able to add methods not in the base class, and register them with this function, so I think I need T, I can't just write Xyz. But what is the cleanest way to constrain usage to such subclasses? – Swiss Frank Jul 22 '20 at 07:46
  • Probably the easiest is `static_assert(std::is_base_of_v);` as the first line in `SubscribeTimer`. Though frankly I don't see the point. If you want to be able to call any method, including those not inherited from `Xyz`, why do you even care that the class is derived from `Xyz`? What would stop working if it isn't? – Igor Tandetnik Jul 22 '20 at 10:06
  • this is a multithreaded application. The actual callback might be a network packet that is of interest to thousands of objects subclassed from Xyz. Rather than the thread that gets the incoming data call those objects directly and sequentially, it queues the callback in message queues that only objects of class Xyz have. – Swiss Frank Jul 22 '20 at 10:20
  • Then you probably need to save `Xyz*` pointer somewhere, in addition to the callback (so that you can obtain the message queue from it). Once you have `Xyz* p = pthis;` or similar, that will effectively require that `T` derive from `Xyz`, or this line won't compile. – Igor Tandetnik Jul 22 '20 at 10:38
  • I'm doing exactly that! – Swiss Frank Jul 22 '20 at 11:11