C++ callback as member function

Fórumok

Sziasztok,

Arduino alapokon szeretnék programot írni ESP32-re. MQTT protokollra szeretnék egy objektumot készíteni, hogy minden egységbe legyen zárva.
A következő problémába futottam bele: MQTT / PubSubclient librari alkalmazása esetén vagy lehet egy callback metódust alkalmazni, ami az üzenet érkezésekor hívódik meg.
Ez azonban alap esetben nem lehetséges, ha a callback metódus sima member function. Lehet objektumon kívüli vagy static, de akkor elvesztek egy pár objektum adta lehetőséget.

Az objektum a következő:
https://pastebin.com/tT8nNvWC

Sok keresés után találtam is megoldásokat, de ezek valahogy nem működnek.
Sajnos nem vagyok egy C guru, ezért fordulok hozzátok.

Tehát a mellékelt kódból kiemelve a megoldásokat és a hibákat:

  // this -> client.setCallback(callback);
            // Problem: argument of type "void (Mqtt::*)(char *topic, uint8_t *payload, unsigned int length)" is incompatible with parameter of type "void (*)(char *, uint8_t *, unsigned int)"C/C++(167)
 
            // this -> client.setCallback ( [this] (char* topic, uint8_t* payload, unsigned int length) { this->callback(topic, payload, length); });
            // Problem: no suitable conversion function from "lambda []void (char *topic, uint8_t *payload, unsigned int length)->void" to "void (*)(char *, uint8_t *, unsigned int)" existsC/C++(413)
             
            // client.setCallback(std::bind(&Mqtt::callback));
            // Problem no suitable conversion function from "std::_Binder<std::_Unforced, void (Mqtt::*)(char *topic, uint8_t *payload, unsigned int length)>" to "void (*)(char *, uint8_t *, unsigned int)" existsC/C++(413)

Tudna valaki segíteni?

 

üdv: redman

 

Hozzászólások

Az elso namespace-gondnak tunik. Lehet hogy az megoldja a tobbit is.

Ez tipikus példa a rosszul megdesignolt API-ra. Ha már C módon gondolkodnak, akkor callbacknek kéne valami "void *userdata" jellegű paraméterének lennie, hogy át tudd adni az osztályod címét. Vagy használhatnának std::function-t a C fvptr helyett.

Szerintem ezzel sokat nem tudsz csinálni. Ha csak 1 példány van az osztályodból, akkor egy globális változón keresztül tudsz rá hivatkozni a callbackből. Ha több példány van, akkor valszeg nincs normális megoldás erre (esetleg, ha tudod, hogy melyik object callback-jét várod következőnek, akkor globális változóba be tudod tenni).

> akkor valszeg nincs normális megoldás erre

Dehogynem 1: Lérehozol egy rakás függvényt kódgenerátorral, annyit, hogy minden callbacknek másikat tudj adni. Berakod őket egy tömbbe, és onnan használod őket index szerint. Minden fv azzal kezdődik, hogy meghívja a generikus handlert egy indexszel. Az index alapján pedig egy tömbben eléred a hozzá tartozó ojjektumot.

fvpointer [1000] = {fv1, fv2, ...};
ojjekt [1000];

void fv1()
{
 callback(ojjekt[1]);
}
void fv2()
{
 callback(ojjekt[2]);
}
void fv3()
{
 callback(ojjekt[3]);
}
void fv4()
{
 callback(ojjekt[4]);
}
...
static nextIndex = 0;

registerCallback(ojekt)
{
ojjekt[nextIndex]=ojekt;
register(fvpointer[nextIndex++]);
}

Dehogynem 2: Debuggerrel megnézed, hogy mennyivel kell visszalépkedni a stacken, hogy kiolvashass valami azonosítót, hiszen a hívó program tuti, hogy tárol valahol valamit. Beteszed a mágikus konstansokat a programba, és simán kiolvasod az értéket pl így:

void callback()
{
 int dummy;
 int * ptr=dummy+mágikusszám;
 int azonosító=*ptr;
 ...
}

 

Két teljesen normális megoldás is van a problémára :-) Ha többet gondolkodnék talán jönne még hasonló...

Maradjunk annyiban, hogy ez eléggé szubjektív, hogy ezek a megoldások normálisak-e. Tutira nem csinálnám egyiket se. Akkor inkább már az eredeti libet módosítanám, ha jól nézem megvan a forráskódja, valszeg eléggé egyszerű beletenni egy "void *userdata" pointert a callbackbe.