从方法指针动态创建通用函数指针,推导出返回值和参数

发布于 2024-12-23 17:25:04 字数 2395 浏览 1 评论 0原文

我有这个辅助类,用于调用需要静态 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,以及 变量 返回类型的回调和一个或多个不同类型 的参数(最后一个这是一个void *userData),我怎样才能用尽可能少的样板,将一些东西传递给回调并让它依次调用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 的有效实现是什么?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦幻的味道 2024-12-30 17:25:05

我也有同样的问题。我想将“指向类成员方法的指针”转换为“指向函数的指针”,因为 Windows API 回调都是“指向函数的指针”。我使用 X86-64 编译器,并且只有一种调用约定。当使用“指向类成员方法的指针”时,传递给函数的第一个参数是“this”,因此当您想将其转换为“指向函数的指针”时,您必须考虑将“this”传递给您的函数。 “指向类成员方法的指针”作为第一个参数,然后传递其他参数。我的方法通过在虚拟地址空间中动态创建可执行二进制代码来实现此目的。请记住,它适用于最多具有 3 个参数的函数,而且它不支持浮点参数作为函数的输入参数。 (因为浮点参数是通过 XMM0、XMM1、XMM2、XMM3 寄存器传输的)。

#include <windows.h>
#include <cstdio>

const byte jumper[] = {
    // mov r9,r8; mov r8,rdx; mov rdx,rcx
    0x4D, 0x89, 0xC1, 0x49, 0x89, 0xD0, 0x48, 0x89, 0xCA, 
    // movabs rcx,0x123456789abcdef; this = 0x123456789abcdef;
    0x48, 0xB9,
    0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01, 
    // movabs rax,0x123456789abcdef; function_pointer = 0x123456789abcdef
    0x48, 0xB8, 
    0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01, 
    // jmp    rax
    0xFF, 0xE0
};

template <typename TSrc, typename TSrcThis, typename TDest>
class CallbackConvertor {
    typedef union  {
        void* pointer;
        byte* byte_pointer;
        TSrc src;
        TDest dest;    
    } FunctionPointer;
    public:
        FunctionPointer dest;
        CallbackConvertor(TSrc src, TSrcThis& self) {
            dest.pointer = VirtualAlloc(NULL,sizeof(jumper), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
            memcpy(dest.pointer,jumper,sizeof(jumper));
            void* s = &self;
            memcpy( &dest.byte_pointer[11]  , &s, 8);
            FunctionPointer pSrc = {.src = src};
            //pSrc.src = src;
            memcpy( &dest.byte_pointer[21] ,&pSrc.pointer,8);
        }
        ~CallbackConvertor() {
            VirtualFree(dest.pointer,0,MEM_RELEASE);
        }
        TDest Result() {
            return dest.dest;
        }
};


class TestClass {
    public:
        int data = 0;
        TestClass() {
            data = 10;
        }
        int OneParameter(int First){
            printf("Called with %d and this.data = %d\n",First,data);
            return 101;
        }

        int TwoParameter(int First, int Second){
            printf("Called with (%d,%d) and this.data = %d\n",First,Second,data);
            return 102;
        }
        int ThreeParameter(int First, int Second, int Third){
            printf("Called with (%d,%d,%d) and this.data = %d\n",First,Second,Third,data);
            return 103;
        }
};

int main() {
    TestClass test;
    CallbackConvertor<int(TestClass::*)(int),TestClass,int(*)(int)> OneParam(&test.OneParameter,test);
    int p = OneParam.Result()(11);
    printf("Result = %d\n",p);
    test.data = 2;
    OneParam.Result()(12);
    CallbackConvertor<int(TestClass::*)(int,int),TestClass,int(*)(int,int)> TwoParam(&test.TwoParameter,test);
    TwoParam.Result()(13,14);
    test.data = 3;
    TwoParam.Result()(15,16);
    CallbackConvertor<int(TestClass::*)(int,int,int),TestClass,int(*)(int,int,int)> ThreeParam(&test.ThreeParameter,test);
    ThreeParam.Result()(17,18,19);
    test.data = 4;
    ThreeParam.Result()(20,21,22);
}

I had the same problem. I wanted to cast "pointer to a class member method" to "pointer to a function" as Windows API callbacks are all "pointer to a function". I work with X86-64 compiler and there is only one calling conventions. When using "pointer to a class member method", the first parameter passed to the function is "this", so when you want to cast it to a "pointer to a function" you have to consider "this" to be passed to your "pointer to a class member method" as the first argument and then the other arguments are passed. My method does this by creating executable binary code in virtual address space on the fly. Remember that it works with functions with at most 3 parameters and also it does not support floating-point arguments as the input arguments of functions. (Because floating-point arguments are transferred by XMM0, XMM1, XMM2, XMM3 registers).

#include <windows.h>
#include <cstdio>

const byte jumper[] = {
    // mov r9,r8; mov r8,rdx; mov rdx,rcx
    0x4D, 0x89, 0xC1, 0x49, 0x89, 0xD0, 0x48, 0x89, 0xCA, 
    // movabs rcx,0x123456789abcdef; this = 0x123456789abcdef;
    0x48, 0xB9,
    0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01, 
    // movabs rax,0x123456789abcdef; function_pointer = 0x123456789abcdef
    0x48, 0xB8, 
    0xEF, 0xCD, 0xAB, 0x89, 0x67, 0x45, 0x23, 0x01, 
    // jmp    rax
    0xFF, 0xE0
};

template <typename TSrc, typename TSrcThis, typename TDest>
class CallbackConvertor {
    typedef union  {
        void* pointer;
        byte* byte_pointer;
        TSrc src;
        TDest dest;    
    } FunctionPointer;
    public:
        FunctionPointer dest;
        CallbackConvertor(TSrc src, TSrcThis& self) {
            dest.pointer = VirtualAlloc(NULL,sizeof(jumper), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
            memcpy(dest.pointer,jumper,sizeof(jumper));
            void* s = &self;
            memcpy( &dest.byte_pointer[11]  , &s, 8);
            FunctionPointer pSrc = {.src = src};
            //pSrc.src = src;
            memcpy( &dest.byte_pointer[21] ,&pSrc.pointer,8);
        }
        ~CallbackConvertor() {
            VirtualFree(dest.pointer,0,MEM_RELEASE);
        }
        TDest Result() {
            return dest.dest;
        }
};


class TestClass {
    public:
        int data = 0;
        TestClass() {
            data = 10;
        }
        int OneParameter(int First){
            printf("Called with %d and this.data = %d\n",First,data);
            return 101;
        }

        int TwoParameter(int First, int Second){
            printf("Called with (%d,%d) and this.data = %d\n",First,Second,data);
            return 102;
        }
        int ThreeParameter(int First, int Second, int Third){
            printf("Called with (%d,%d,%d) and this.data = %d\n",First,Second,Third,data);
            return 103;
        }
};

int main() {
    TestClass test;
    CallbackConvertor<int(TestClass::*)(int),TestClass,int(*)(int)> OneParam(&test.OneParameter,test);
    int p = OneParam.Result()(11);
    printf("Result = %d\n",p);
    test.data = 2;
    OneParam.Result()(12);
    CallbackConvertor<int(TestClass::*)(int,int),TestClass,int(*)(int,int)> TwoParam(&test.TwoParameter,test);
    TwoParam.Result()(13,14);
    test.data = 3;
    TwoParam.Result()(15,16);
    CallbackConvertor<int(TestClass::*)(int,int,int),TestClass,int(*)(int,int,int)> ThreeParam(&test.ThreeParameter,test);
    ThreeParam.Result()(17,18,19);
    test.data = 4;
    ThreeParam.Result()(20,21,22);
}
嘿哥们儿 2024-12-30 17:25:04

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

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

请注意,因为 myClass 是一个 std::shared_ptr<> 并通过值捕获,即使 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
);

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
);
吾性傲以野 2024-12-30 17:25:04

看看这是否是您想要的。您仍然必须指定返回类型,但(在我看来)它很好地指定为保存 static 包装函数的 struct 的模板参数。如果您需要更高程度的灵活性 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...只是为了澄清,您需要定义一个模板化 Call< /code> 函数用于具有相同数量参数的所有回调

See if this is what you want. You still have to specify return type, but it's nicely (in my opinion) specified as a template parameter to struct that hold static wrapper functions. You can still improve it if you need higher degree of flexibility for TTst - I'm not sure how you want to define member functions to be called, so I kept their signature as callback's.

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

EDIT: modified arguments to Bar01 - I didn't notice it accepts 2 arguments as Bar00... Just to clarify, you need to define one templated Call function for all Callback's that have the same number of arguments.

怎会甘心 2024-12-30 17:25:04

编辑:这并不完全是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;为了简单起见,使用 voidFunction 。

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

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

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

EDIT: This is not quite what the OP needs. The OP needs a generic version of this.

Why not use a std::function all the way through?

It would look like

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

runStdFunction will then be as simple as pie

I am using typedef std::function<void(void)> voidFunction for simplicity.

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

The idea is simple, all you are doing is passing std::functions through and then calling them.
Leave all of the converting to/from member functions/etc to std::bind.

Of course your return type is not void, but that's a minor detail.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文