从方法指针动态创建泛型函数指针,推导返回值和参数

时间:2011-12-28 21:52:51

标签: c++ callback c++11 std-function generic-callback

我有这个帮助器类,我用它来调用期望静态C函数的代码的成员方法。这个特殊的“版本”与Windows LPTHREADROUTINE回调兼容,将DWORD (class::method) (void *)函数作为参数,如下所示:

CreateThread(NULL, 0, runThreadFunction<SomeClass>, makeThreadInfo(&myClass, &SomeClass::ThreadFunction, NULL), 0, NULL);

我希望整个事情都是通用的,我知道它可以用新的C ++ 11标准来完成,但是我无法将其完全删除。

#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);
}

我尝试将MakeThreadInfo函数的界面更改为:

template <typename C, typename R, typename... P>
void* MakeThreadInfo(C* o, std::function<R(P&...)> f, void* d)

这似乎是要走的路,但我无法将此值传递到上游。


这是我想要的:

给定一个带有MyMethod方法的MyClass类,以及一个 variable 的回调返回类型和不同类型的一个或多个参数(最后一个是{{ 1}}),我怎么能用尽可能少的boeplating将某些传递给回调并让它依次调用MyClass :: MyMethod。

举例说明:

void *userData

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); } } 的有效实施是什么?

3 个答案:

答案 0 :(得分:1)

我并不完全清楚你正在寻找什么级别的通用性,但也许这会让你开始:

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
);

请注意,因为myClassstd::shared_ptr<>并且是按值捕获的,所以即使SomeClass超出范围之前,基础myClass的生命周期也会正常终止线程完成执行(,只要 RunThreadFunction最终被调用)。


编辑:这是另一种方法(未经测试,可能是语法错误):

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
);

答案 1 :(得分:1)

看看这是不是你想要的。您仍然需要指定返回类型,但很好(在我看来)指定为struct的模板参数,其中包含static包装函数。如果你需要TTst更高的灵活性,你仍然可以改进它 - 我不确定你要如何定义要调用的成员函数,所以我将它们的签名保持为callback。 / p>

#include <iostream>

typedef int (*TFoo00)( void * );
typedef bool (*TFoo01)( int, void * );

void Bar00( TFoo00 fnc, void * ud )
{
 std::cout << "Bar00" << std::endl;
 fnc( ud );
}
void Bar01( TFoo01 fnc, void * ud )
{
 std::cout << "Bar01 " << std::endl;

 fnc( -1, ud );
}

class TTst;

template< typename PResult >
struct TWrap
{

  static PResult Call( void * ud )
  {
   std::cout << "TWrap::Call( P00 )" << std::endl;
   return ( static_cast< TTst * > ( ud )->Foo00() );
  }
  template< typename P00 >
  static PResult Call( P00 u00, void * ud )
  {
   std::cout << "TTst::Call( P00, P01 )" << std::endl;
   return ( static_cast< TTst * > ( ud )->Foo01( u00 ) );
  }
};

class TTst
{
 public:
  int Foo00( void )
  {
   std::cout << "TTst::Foo00" << std::endl;
   return ( 0 );
  }
  bool Foo01( int u00 )
  {
   std::cout << "TTst::Foo01 : "  << u00 << std::endl;
   return ( u00 != 0 );
  }

  void Do( void )
  {
   Bar00( TWrap< int >::Call, this );
   Bar01( TWrap< bool >::Call, this );
  }

};

int main( void )
{
 TTst lT;

 lT.Do();

 return ( 0 );
}

编辑:修改Bar01的参数 - 我没有注意到它接受2个参数为Bar00 ...只是为了澄清,你需要为所有人定义一个模板Call函数Callback具有相同数量的参数。

答案 2 :(得分:0)

编辑:这不是OP需要的。 OP需要通用版本。

为什么不一直使用std :: function?

看起来像

std::function<void(void)> myFunc = std::bind(&Class::memberFunc,&classInstance);
CreateThread(NULL,0,runStdFunction, new std::function<void(void)>(myFunc),0,NULL);
然后,

runStdFunction将像pie

一样简单

为简单起见,我使用typedef std::function<void(void)> voidFunction

void runStdFunction(void *data)
{
   std::unqiue_ptr<voidFunction> func (static_cast<voidFunction*>(data));
   (*func)();
}

这个想法很简单,你所做的只是传递std :: functions然后调用它们。 将所有转换成/从成员函数/ etc转到std :: bind。

当然,您的返回类型不是空的,但这是一个小细节。