2 Stimmen

Dynamische Erstellung von generischen Funktionszeigern aus Methodenzeigern, Ableitung von Rückgabewert und Parametern

Ich habe diese Hilfsklasse, die ich verwende, um Membermethoden für Code aufzurufen, der statische C-Funktionen erwartet. Diese spezielle "Version" ist kompatibel mit Windows LPTHREADROUTINE-Aufrufen und nimmt eine DWORD (class::method) (void *) Funktion als Parameter, die wie folgt aufgerufen wird:

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

Ich möchte das Ganze generisch machen, und ich weiß, dass das mit dem neuen C++11-Standard möglich ist, aber ich bin nicht in der Lage, es durchzuziehen.

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

Ich habe versucht, die Schnittstelle der MakeThreadInfo-Funktion in etwas wie dieses zu ändern:

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

Das scheint der richtige Weg zu sein, aber ich war nicht in der Lage, diesen Wert weiterzugeben.


Ich möchte auf Folgendes hinaus:

Gegeben eine Klasse MyClass mit einer Methode MyMethod und einem Callback von variabel Rückgabetyp und einem oder mehreren Parametern von unterschiedliche Arten (letzteres ist ein void *userData ), wie kann ich mit so wenig Kesselflickerei wie möglich die etwas an den Callback übergeben und diesen wiederum MyClass::MyMethod aufrufen lassen.

Zur Veranschaulichung:

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

Was ist eine gültige Implementierung von CreateGenericCPointer ?

1voto

ildjarn Punkte 61204

Es ist mir nicht ganz klar, welchen Grad an Allgemeinheit Sie suchen, aber vielleicht hilft Ihnen das auf die Sprünge:

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

Beachten Sie, dass myClass ist eine std::shared_ptr<> und wird durch den Wert erfasst, der zugrunde liegt SomeClass ordnungsgemäß beendet wird, auch wenn myClass den Anwendungsbereich verlässt, bevor die Ausführung des Threads beendet ist ( so lange wie RunThreadFunction wird schließlich aufgerufen).


EDITです。 Hier ist ein anderer Ansatz (ungetestet, kann Syntaxfehler enthalten):

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

1voto

lapk Punkte 3758

Schauen Sie, ob es das ist, was Sie wollen. Sie müssen immer noch den Rückgabetyp angeben, aber es ist schön (meiner Meinung nach) als ein Template-Parameter für struct die halten static Wrapper-Funktionen. Sie können es immer noch verbessern, wenn Sie ein höheres Maß an Flexibilität benötigen für TTst - Ich bin mir nicht sicher, wie Sie die aufzurufenden Mitgliedsfunktionen definieren wollen, daher habe ich ihre Signatur als 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: Argumente geändert in Bar01 - Ich habe nicht bemerkt, dass es 2 Argumente akzeptiert als Bar00 ... Nur zur Klarstellung: Sie müssen eine Vorlage definieren Call Funktion für alle Callback die die gleiche Anzahl von Argumenten haben.

0voto

EDITです。 Das ist nicht ganz das, was der Auftraggeber braucht. Der OP braucht eine allgemeine Version davon.

Warum nicht durchgängig eine std::function verwenden?

Sie würde wie folgt aussehen

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

runStdFunction wird dann ganz einfach sein

Ich verwende typedef std::function<void(void)> voidFunction der Einfachheit halber.

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

Die Idee ist einfach: Alles, was Sie tun, ist, std::functions durchzugeben und sie dann aufzurufen. Überlassen Sie die Konvertierung in/aus Mitgliedsfunktionen/etc. std::bind.

Natürlich ist Ihr Rückgabetyp nicht ungültig, aber das ist ein kleines Detail.

0voto

Mehrdad Nazmdar Punkte 164

Ich hatte das gleiche Problem. Ich wollte "Zeiger auf eine Klassenmethode" in "Zeiger auf eine Funktion" umwandeln, da alle Windows-API-Rückrufe "Zeiger auf eine Funktion" sind. Ich arbeite mit einem X86-64-Compiler und es gibt nur eine Aufrufkonvention. Wenn Sie "Zeiger auf eine Klassenmethode" verwenden, ist der erste Parameter, der an die Funktion übergeben wird, "this". Wenn Sie ihn also in einen "Zeiger auf eine Funktion" umwandeln wollen, müssen Sie "this" als erstes Argument an Ihren "Zeiger auf eine Klassenmethode" übergeben, und dann werden die anderen Argumente übergeben. Meine Methode tut dies, indem sie ausführbaren Binärcode im virtuellen Adressraum in Echtzeit erzeugt. Denken Sie daran, dass sie mit Funktionen mit höchstens 3 Parametern funktioniert und auch keine Fließkomma-Argumente als Eingangsargumente von Funktionen unterstützt. (Denn Fließkomma-Argumente werden von den Registern XMM0, XMM1, XMM2 und XMM3 übertragen).

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X