【问题标题】:Dynamically create generic function pointer from method pointer, deducing return value and params从方法指针动态创建泛型函数指针,推导出返回值和参数
【发布时间】:2011-12-28 21:52:51
【问题描述】:

我有这个助手类,我用它来调用需要静态 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 的回调和一个或多个 variing types 的参数(最后一个是 @987654326 @),我怎样才能尽可能少地进行样板化,将 something 传递给回调,然后让它调用 MyClass::MyMethod。

举例说明:

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

CreateGenericCPointer 的有效实现是什么?

【问题讨论】:

  • 使MakeThreadInfo 通用的目标是什么?你想支持函子吗?
  • 我想将它与具有十几种不同回调格式的 C 库结合使用。我不想为每个类都复制这个类,而是想修改它以自动为动态创建的回调推断正确的返回和参数类型。
  • 你的意思是你想让MakeThreadInfo 为可能接受void* 以外的参数并返回DWORDs 以外的东西的方法工作?请注意,SO 不是论坛,cmets 不用于讨论。澄清应该编辑到问题中。
  • Class method as winAPI callback 的可能重复项
  • @sbi:不,不是。查看我的编辑。

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


【解决方案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&lt;&gt; 并且是按值捕获的,所以即使myClass 在线程完成执行之前超出范围,底层SomeClass 的生命周期也会正确终止(只要 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
);

【讨论】:

  • 是的,我正在寻找比这更通用的。请看我的编辑。基本上,应该推导出 DWORD 以及任何其他可能的参数。
  • @Mahmoud :所有参数都已绑定在 lambda 中,因此 std::function&lt;&gt; 将始终为空;即,没有没有个论据可以推断。唯一剩下的是返回类型,并且从根本上推断这是不可能的,因为RunThreadFunction 需要从void* 转换为具体类型。这已经和你可以得到 AFAIK 一样通用。
  • ildjarn:您的更新使其非常接近我的想法。我在我的一个实现中具有模板化的返回值,但我几乎完全确定也可以实现可变参数。我现在必须用完,但如果没有其他人有任何建议,我想我今晚会花一些时间来解决这个问题。
  • @Mahmoud :但是,为什么需要可变参数? lambda 使用任何必要的参数调用您真正的底层成员函数,并且 lambda 始终为空值,因此生成的 std::function&lt;&gt; 始终为空值。你需要什么是 lambda 无法完成的?
  • 避免每次都显式声明f。虽然作为一个局部变量等等,如果这是最好的,这是可以接受的,但我不禁觉得有一个解决方案可以跳过这个完整的 C++11 规范使这一步成为可能。
【解决方案2】:

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

#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... 澄清一下,您需要为所有 Callback 定义一个模板化 Call 函数具有相同数量的参数。

【讨论】:

    【解决方案3】:

    编辑:这不是 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 就会像饼图一样简单

    为了简单起见,我使用typedef std::function&lt;void(void)&gt; voidFunction

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

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

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

    【讨论】:

    • 查看我的编辑,了解为什么这不是我正在寻找的理想解决方案。
    • @MahmoudAl-Qudsi 哦,我明白了,你想要一个通用版本的 runStdFunction。这是一个更有趣(也更困难)的问题。我敢打赌,正确的答案将涉及复杂的可变参数模板。
    猜你喜欢
    • 2023-02-04
    • 2020-07-30
    • 2018-05-30
    • 2019-08-22
    • 1970-01-01
    • 1970-01-01
    • 2016-01-12
    • 1970-01-01
    • 2018-07-27
    相关资源
    最近更新 更多