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.stackexchange.com/questions/14730/impossibly-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ść.

0

void (*callback)() = NULL;

To jest zwyczajny wskaźnik do funkcji globalnej - adres po prostu,
zatem nie dziwota że nie można do tego przypisać adresu metody z jakiejś tam class...
być może takie coś nawet przejdzie, ale co najwyżej do statycznej metody.

https://msdn.microsoft.com/en-us/library/yyh8hetw.aspx

2

Zamiast męczyć się z ze wskaźnikami na funkcję korzystaj z interface'ów.

https://ideone.com/rbYSwI

3

Uzupełniając to, co zostało już powiedziane.

#include <iostream>
#include <functional>
using namespace std;

struct First{
	void callback() const{
		cout << "foo" << endl;
	}
};

struct Second{
	void callback() const{
		cout << "bar" << endl;
	}
};

int main(){
	using callback_type = function<void()>;
	callback_type callbacks[] = {
		bind(&First::callback, First{}),
		bind(&Second::callback, Second{})
	};
	
	for(auto callback : callbacks)
		callback();
}
1

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

Teraz pokaż jak implementujesz to na AVR. Czy Wy w ogóle czytacie treść wątków, w których piszecie?

To na AVR std::function nie ma albo nie działa?
Ja bym nie przekreślał tak z góry, tylko napisał ten kod, a dopiero jak się okaże że program się nie mieści, zastanawiał co z tym fantem zrobić.

2

No tak. Nie ma i nie działa.

Nie ma libstdc++. A inne implementacje są krzywe, mocno niedorobione i stare. Nie ma std::function. Nie ma new nawet, bo to też część biblioteki standardowej - trzeba sobie samemu napisać (Arduino dostarcza implementację new). Samo Arduino też jest krzywe i niedorobione tak btw.

Na AVR jest jedynie prawie pełna biblioteka standardowa C, która jest powszechnie znana i używana (AVR Libc). Tak samo dobrej biblioteki C++ nie ma (są uClibc++, ustl i jeszcze jakaś jedna bez nazwy, ale stare, krzywe i niedorobione właśnie).

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