Funkcja callback w C++

0

Witam wszystkich na forum. Pisząc program w C++ natknąłem się z problemem, którego nie potrafię obejść.

Przykładowy kod:

//plik.h

class A {
    public:
        A();
        void (*callback)() = NULL;
};

class B : public A {
    public:
        B();
        void b_onCallback();
};

//plik.cpp

A::A() { }

B::B() : A() {
    A::callback = &b_onCallback; // Przypisanie funkcji obiektu B nie działa
}

void B::b_onCallback() { }

Generalnie chodzi oto, że potrzebuję wywołać funkcję callback z funkcją globalną i w klasie pochodnej dziedziczonej po klasie A.

Przypisanie funkcji globalnej do callback działa poprawnie.

A objA = A();
objA.callback = &objA_onCallback; // Przypisanie funkcji globalnej działa

void objA_onCallback() { }

W przypadku klasy B dziedziczonej po klasie A zwraca mi błąd:

Error: cannot convert 'void (B::)()' to 'void ()()' in assignment A::callback = &b_onCallback;

Rozumiem skąd jest ten błąd, ale nie wiem jak go rozwiązać.

1

musisz zmienic typ wskaznika

nie lepiej uzyc std::function zamiast wskaznika?

1

Zmień typ wskaźnika na funkcję albo użyj czegoś z type erasure, np std::function

1

Są (niełatwe) sposoby na to, ale twój przypadek jest dziwny. Dlaczego robisz to w klasie pochodnej?
W ten sposób próbujesz wynaleźć polimorfizm na nowo. Nie mógłbyś użyć metody wirtualnej?

0

Nie bardzo mogę skorzystać z std::function, piszę program na Arduino. Ograniczona ilość pamięci 32KB.

W obecnej chwili nie mogę przetestować. Myślicie, że jak przypiszę

A::callback = ( void* )&b_onCallback;

to zadziała?

2

Nie, rzutowanie to jest złe rozwiązanie - funkcje-członkowie klas i wolne funkcje mają różne konwencje wywołania, poza tym to jest zwykłe UB.

Przykład: http://melpon.org/wandbox/permlink/El8tbmKm9gMHoIoB

Jeśli std::function zbyt duże dla Ciebie, to fast delegate powinno być ok: http://codereview.stackexchan[...]possibly-fast-delegate-in-c11

3

Zawsze można zrobić takie coś ale to raczej jako ciekawostka (Undefined behavior):

#include <iostream>
using namespace std;

class Pierwsza {
    public:
        void (Pierwsza::*callback)();
        void wykonajCallback(){ (this->*callback)(); }
};

class Druga : public Pierwsza {
    public:
        Druga(){
            callback = static_cast<void (Pierwsza::*)()>(&Druga::b_onCallback); 
        }
        void b_onCallback(){ cout << "Test" << endl; }
};

int main(){
    Pierwsza *obj = new Druga();
    obj->wykonajCallback();
    delete obj;
    return 0;
}

https://ideone.com/8ccE5D

EDIT Jak się okazuje to nie jest UB ale najzupełniej poprawne podejście.

0

ok rozwiązanie zaproponowane przez grzesiek51114 działa. Nie chciałbym wdawać się w szczegóły, ale zależy mi na przypisywaniu callbacków jako funkcję globalne.
Może mi ktoś powiedzieć dlaczego taka konwersja nie działa?

Pierwsza::callback = static_cast<void (*)()>(&Druga::b_onCallback);
2

Rozwiązanie @grzesiek51114 to jest undefined behavior (5.2.9.12) brzydactwo a nie "działa". To co próbujesz zrobić - jak niby skonwertować wskaźnik na funkcję składową na "zwykłą" funkcję? A co z this?

Będziesz musiał napisać sobie swoje opakowanie na funkcję.

Tak się składa, że sam piszę coś takiego na AVR, możesz poczekać niesprecyzowanie długi czas aż będzie mi się chciało skończyć. :-P

1

Nie działa, bo niestatyczne funkcje klas mają niejawnie przekazywany wskaźnik na instancję klasy this jako jeden z parametrów. Normalna funkcja tego nie ma i stąd inny sposób wywołania. Rozwiązanie @grzesiek51114 to UB¹ (?), jeśli go użyjesz to kiedyś może Ci wybuchnąć w twarz. Nie rób tego w projekcie poważniejszym niż zaliczenie na studia.

Dlaczego nie użyjesz zalinkowanego fast delegate? Nie alokuje nic na stercie.

¹ teraz sam nie wiem czy to UB czy nie. Na pewno jest to bad design, więc sobie odpuść.

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0