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 cast 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 its 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.
Aucun commentaire:
Enregistrer un commentaire