I have this helper class that I use to call member methods for code that's expecting static C functions. This particular "version" is compatible with Windows LPTHREADROUTINE callbacks, taking a DWORD (class::method) (void *)
function as a parameter, called like this:
CreateThread(NULL, 0, runThreadFunction<SomeClass>, makeThreadInfo(&myClass, &SomeClass::ThreadFunction, NULL), 0, NULL);
I wish to make the entire thing generic, and I know it can be done with the new C++11 standard, but I'm unable to pull it off.
#pragma once
#include <stdafx.h>
template <typename C>
struct ThreadInfo
{
// we have an object
C* obj;
// and that object has a function that takes void* and returns DWORD, and so is suitable for a threadproc (except for it being a member function)
DWORD (C::* function)(void*);
// and we have any amount of extra data that we might need.
void* data;
// default copy c-tor, d-tor and operator= are fine
ThreadInfo(C* o, DWORD (C::*func)(void*), void* d) : obj(o), function(func), data(d)
{
}
};
template <typename C>
DWORD WINAPI RunThreadFunction(void* data)
{
shared_ptr<ThreadInfo<C> > ti((ThreadInfo<C>*)data);
//ThreadInfo<C>* ti = (ThreadInfo<C>*) data;
return ((ti->obj)->*(ti->function))(ti->data);
}
template <typename C>
void* MakeThreadInfo(C* o, DWORD (C::* f)(void*), void* d)
{
return (void*)new ThreadInfo<C>(o, f, d);
}
I've tried changing the interface of the MakeThreadInfo function to something like this:
template <typename C, typename R, typename... P>
void* MakeThreadInfo(C* o, std::function<R(P&...)> f, void* d)
Which would seem to be the way to go, but I was unable to then pass this value upstream.
Here's what I want to get at:
Given a class MyClass with a method MyMethod, and a callback of variable return type and one or more parameters of varying types (the last of which is a void *userData
), how can I, with as little boilerplating as possible, pass something to the callback and have it in turn call MyClass::MyMethod.
To illustrate:
typedef bool (*Callback1)(void *userData);
typedef int (*Callback2)(bool param, void *userData);
void TheirLibrary::Function1(Callback1 callback, void *userData);
void TheirLibrary::Function2(Callback2 callback, void *userData);
class MyClass
{
bool MyMethod1(void *userData);
int MyMethod2(bool someParam, void *userData);
void DoSomething()
{
Function1(CreateGenericCPointer(&MyClass::MyMethod1), &MyClass);
Function2(CreateGenericCPointer(&MyClass::MyMethod2), &MyClass);
}
}
What's a valid implementation of CreateGenericCPointer
?
It's not entirely clear to me what level of genericity you're looking for, but maybe this will get you started:
typedef std::function<DWORD()> ThreadFuncT;
DWORD WINAPI RunThreadFunction(void* data)
{
std::unique_ptr<ThreadFuncT> tf(static_cast<ThreadFuncT*>(data));
return (*tf)();
}
template<typename F>
ThreadFuncT* MakeThreadFunction(F&& f)
{
return new ThreadFuncT(std::forward<F>(f));
}
// ...
auto myClass = std::make_shared<SomeClass>(/* ... */);
CreateThread(
nullptr,
0,
RunThreadFunction,
MakeThreadFunction([=]() { return myClass->ThreadFunction(nullptr); }),
0,
nullptr
);
Note that because myClass
is a std::shared_ptr<>
and is captured by value, the underlying SomeClass
's lifetime will terminate properly even if myClass
goes out of scope before the thread is finished executing (as long as RunThreadFunction
is eventually called).
EDIT: Here's another approach (untested, may be syntax errors):
template<typename R>
R WINAPI RunThreadFunction(void* data)
{
typedef std::function<R()> ThreadFuncT;
std::unique_ptr<ThreadFuncT> tf(static_cast<ThreadFuncT*>(data));
return (*tf)();
}
template<typename F>
auto MakeThreadFunction(F&& f) -> std::function<decltype(f())()>*
{
return new std::function<decltype(f())()>(std::forward<F>(f));
}
// ...
auto myClass = std::make_shared<SomeClass>(/* ... */);
auto f = [=]() { return myClass->ThreadFunction(nullptr); };
CreateThread(
nullptr,
0,
RunThreadFunction<decltype(f())>,
MakeThreadFunction(std::move(f)),
0,
nullptr
);