Beste metoden for å ha en C ++ medlem funksjons bli oppringt av en C tilbakeringing?

stemmer
4

Gitt en typisk klasse:

struct Uansett
{
    ugyldig Doit ();
};

Uansett w;

hva er den beste måten å få medlemsfunksjonen til å bli oppringt av en C tomrom * basert tilbakeringing som pthread_create () eller et signal handler?

pthread_t pid;

pthread_create (& pid, 0, ... og w.Doit () ...);
Publisert på 26/09/2008 klokken 15:25
kilden bruker
På andre språk...                            


9 svar

stemmer
6

De fleste C tilbakeanrop tillate å spesifisere et argument for eksempel

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                   void *(*start_routine)(void*), void *arg);

Så du kan ha

void myclass_doit(void* x)
{
  MyClass* c = reinterpret_cast<MyClass*>(x);
  c->doit();
}

pthread_create(..., &myclass_doit, (void*)(&obj));
Svarte 26/09/2008 kl. 15:30
kilden bruker

stemmer
5

Den mest konsise løsningen er å definere, i en header fil deles av alle koden din:

mal <type-T, void (T :: * M) ()>
void * thunk (
    void * p)
{
    T * pt = static_cast <T *> (p);

    (Pt -> * M) ();

    returnere 0;
}

Du ønsker sannsynligvis å definere 4 versjoner: en hver hvor thunk returnerer ugyldig og annullert *, og en hver der medlemmet funksjonen returnerer ugyldig og annullert *. På den måten kompilatoren kan matche den beste, avhengig av omstendighetene (og faktisk det vil klage hvis alt stemmer ikke.)

Så alt du trenger å skrive inn hver gang du kjører inn i en av disse situasjonene er:

pthread_create (& pid, 0, & thunk <Uansett, og uansett :: doit>, & w);

Dette vil også arbeide når metoden er privat, så lenge metoden er referert fra innenfor klassen kode. (Hvis ikke, må jeg lurer på hvorfor koden henviser til et privat metode.)

Svarte 26/09/2008 kl. 19:51
kilden bruker

stemmer
3

Er medlem funksjonen privat? Hvis ikke, kan du bruke standard idiom:

void* pthread_foo_caller(void* arg) {
    Foo* foo = static_cast<Foo*>(arg);
    foo->bar();
    return NULL;
}

Hvis medlemmet funksjon er privat, kan du erklære en statisk metode i klassen som tar en "dette" peker og kaller den riktige metoden. For eksempel:

class Foo {
 public:
  static pthread_foo_caller(void* arg);
  ...
};

void* Foo::pthread_foo_caller(void* arg) {
    Foo* foo = static_cast<Foo*>(arg);
    foo->private_bar();
    return NULL;
}
Svarte 26/09/2008 kl. 15:34
kilden bruker

stemmer
3

Bruk en C-funksjon wrapper som dette:

struct Whatever
{
    void Doit();
};

extern "C" static int DoItcallback (void * arg)
{
  Whatever * w = (Whatever *) arg;
  w->DoIt();
  return something;
}

Fungerer bare hvis du kan passere pekeren til klassen eller annen måte. De fleste tilbakeringing mekanismer tillate dette.

AFAIK dette er den eneste måten å gjøre dette. Du kan ikke kalle en metode fra C uten mye hacking.

Svarte 26/09/2008 kl. 15:30
kilden bruker

stemmer
1

En ting du bør være klar over er at hvis du skriver kode som dette:

try {
    CallIntoCFunctionThatCallsMeBack((void *)this, fCallTheDoItFunction);
} catch (MyException &err)
{
   stderr << "badness.";
}

void fCallTheDoItFunction(void *cookie)
{
    MyClass* c = reinterpret_cast<MyClass*>(cookie);
    if (c->IsInvalid())
        throw MyException;
    c->DoIt();
}

Du kan kjøre inn i noen alvorlige problemer avhengig av kompilatoren. Det viser seg at i noen kompilatorer mens optimalisere, de ser en eneste C samtale i en prøve / catch-blokken og utbryte med glede, "jeg kaller en C-funksjon som, fordi det er god gammeldags C, kan ikke kaste! Calloo-tisk ! jeg skal fjerne alle rester av prøve / fangst siden det aldri vil bli nådd.

Silly kompilatoren.

Ikke kall inn C som ringer deg tilbake og forventer å være i stand til å fange.

Svarte 26/09/2008 kl. 20:10
kilden bruker

stemmer
1

Her er en enkel måte å gjøre det, ikke glem å administrere levetiden på "MemberFunction" objekt riktig.

#include 

class MyClass
{
public:
    void DoStuff()
    {
        printf("Doing Stuff!");
    }
};

struct MemberFunction
{
    virtual ~MemberFunction(){}
    virtual void Invoke() = 0;
};

void InvokeMember(void *ptr)
{
    static_cast(ptr)->Invoke();
}

template 
struct MemberFunctionOnT : MemberFunction
{
    typedef void (T::*function_t)();
public:
    MemberFunctionOnT(T* obj, function_t fun)
    {
        m_obj = obj;
        m_fun = fun;
    }

    void Invoke()
    {
        (m_obj->*m_fun)();
    }
private:
    T *m_obj;
    function_t m_fun;
};

template 

MemberFunction* NewMemberFunction(T *obj, void (T::*fun)())
{ 
    return new MemberFunctionOnT(obj, fun); 
}

//simulate a C-style function offering callback functionality.
void i_will_call_you_later(void (*fun)(void*), void *arg)
{
    fun(arg);
}

int main()
{
    //Sample usage.
    MyClass foo;

    MemberFunction *arg = NewMemberFunction(&foo, &MyClass::DoStuff);
    i_will_call_you_later(&InvokeMember, arg);
    return 0;
}
Svarte 26/09/2008 kl. 16:21
kilden bruker

stemmer
1

Medlemsfunksjonen må være statisk. Non-statiske har en underforstått "dette" argumentet. Før pekeren til din Uansett eksempel som tomrommet * slik at den statiske medlem kan få på forekomsten.

Svarte 26/09/2008 kl. 15:29
kilden bruker

stemmer
0

Selv om jeg ikke har brukt det fra C, for å gjøre tilbakeanrop, jeg anbefaler å se på libsigc ++ . Det har vært akkurat det jeg har behov for en rekke ganger når du gjør C ++ tilbakeanrop.

Svarte 26/09/2008 kl. 15:33
kilden bruker

stemmer
0

Se denne linken

I utgangspunktet er det ikke direkte er mulig, fordi: "pekere til ikke-statiske medlemmer er forskjellig fra vanlige C funksjonspekere ettersom de trenger denne-pekeren av en klasse gjenstand som skal sendes således vanlig funksjonspekere og [pekere til] non-statisk. medlem funksjoner har ulike og inkompatible signaturer"

Svarte 26/09/2008 kl. 15:30
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more