Portál AbcLinuxu, 10. května 2024 08:16


Dotaz: C++ callback

8.11.2019 17:14 __blr__
C++ callback
Přečteno: 849×
Odpovědět | Admin
Zdravím,

potřeboval bych implementovat jednoduchou epoll smyčku, kdy ke každému deskriptoru potřebuju uložit callback. Jak známo, pokud nebudu používat statické metody, není možné použít &trida::metoda.

Dohledal jsem mnoho implementací "delegation/c++ method callback" z různých dob na různých verzích C++.

Rád bych se zeptat, jak dnes, tady a teď řešit tuto problematiku.

Díky
Nástroje: Začni sledovat (0) ?Zašle upozornění na váš email při vložení nového komentáře.

Odpovědi

8.11.2019 20:38 .
Rozbalit Rozbalit vše Re: C++ callback
Odpovědět | | Sbalit | Link | Blokovat | Admin
A to "potřebuju" znamená co?
8.11.2019 22:28 rastos | skóre: 62 | blog: rastos
Rozbalit Rozbalit vše Re: C++ callback
Odpovědět | | Sbalit | Link | Blokovat | Admin
Metóda nejakej triedy v C++ je vlastne obyčajná C funkcia, ktorá okrem obyčajných parametrov dostane jeden parameter navyše: this. Ja som stará škola, ale urobil by som jednoducho mapu, kde kľúčom je deskriptor a hodnotou je pointer na funkciu.

A pokiaľ treba funkcii "odovzdať this", tak by hodnotou v tej mape nebol pointer na funkciu, ale štruktúra, v ktorej je jeden pointer na objekt (,ktorého metódu treba zavolať) a jeden pointer na funkciu.
11.11.2019 12:44 luky
Rozbalit Rozbalit vše Re: C++ callback
Odpovědět | | Sbalit | Link | Blokovat | Admin
https://en.cppreference.com/w/cpp/utility/functional/invoke
11.11.2019 14:16 Andrej | skóre: 51 | blog: Republic of Mordor
Rozbalit Rozbalit vše Re: C++ callback
Odpovědět | | Sbalit | Link | Blokovat | Admin
Jak známo, pokud nebudu používat statické metody, není možné použít &trida::metoda.

Huh? A tohle je "známo" odkdy? Že jsem to nějak nepostřehnul… Tady jsou "nestatické" metody a &třída::metoda:

#include <iostream>
#include <memory>
#include <string>
#include <tuple>

class BunchOfCallbacks {
 public:
  BunchOfCallbacks(std::string state) : state_(std::move(state)) {}

  void Callback1(const std::string& message) const {
    std::cout << "Callback1 (" << state_ << "): " << message << std::endl;
  }
  void Callback2(const std::string& message) const {
    std::cout << "Callback2 (" << state_ << "): " << message << std::endl;
  }

 private:
  const std::string state_;
};

int main() {
  const std::tuple<BunchOfCallbacks,
                   void (BunchOfCallbacks::*)(const std::string&) const>
      callbacks[]{
          {BunchOfCallbacks{"first state"}, &BunchOfCallbacks::Callback1},
          {BunchOfCallbacks{"second state"}, &BunchOfCallbacks::Callback2},
      };

  for (const auto& bunch_callback : callbacks) {
    const auto& bunch{std::get<0>(bunch_callback)};
    const auto callback{std::get<1>(bunch_callback)};

    (bunch.*callback)("Yay! I can select a callback!");
  }
}
xkucf03 avatar 11.11.2019 14:28 xkucf03 | skóre: 49 | blog: xkucf03
Rozbalit Rozbalit vše Re: C++ callback

Bývá problém to předat do API nějaké céčkovské knihovny, ne?

Tam to často řeší tak, že kromě (statické) funkce lze předat i libovolný ukazatel, který se pak použije jako parametr při volání té funkce (tzn. tohle je způsob jak tam dostat to this nebo nějaký kontext)

Mám rád, když se lidé přou, znamená to, že vědí, co dělají, a že mají směr. Frantovo.cz, SQL-DK, Relational pipes
11.11.2019 16:43 Andrej | skóre: 51 | blog: Republic of Mordor
Rozbalit Rozbalit vše Re: C++ callback

Máš pravdu, (implicitní) požadavek na interoperabilitu s C jsem tady úplně přehlédl. :-) Ano, tam by se to muselo řešit jinak.

Problém totiž je, že member pointer je dost podivná věc. Například sizeof(void (BunchOfCallbacks::*)(const std::string&)) je asi tak 16, což zjevně neodpovídá pointeru, se kterým by mohlo pracovat přímo C.

(Důvod pro velikost member pointeru nějak souvisí s tím, že skrz něj musí správně fungovat taky virtuální metody. Což je u tříd bez virtuálních metod celkem jedno, ale ta implementace už je zkrátka taková.)

Když to má spolupracovat s C, asi bych ten callback napsal jako virtuální metodu, tj. každý typ callbacku by byl třída. Tam se pak dá předat tomu C nějaký void* a vhodný C++ wrapper (viditelný z C) ho může interpretovat jako ukazatel na toho abstraktního předka a zavolat jeho virtuální metodu.

11.11.2019 16:56 Andrej | skóre: 51 | blog: Republic of Mordor
Rozbalit Rozbalit vše Re: C++ callback

Inu, epoll() neznám, takže příklad s epoll() tady narychlo nesesmolím, ale takhle by mohla (obecně) vypadat ta spolupráce s C (tady konkrétně s pthread):

#include <errno.h>
#include <pthread.h>
#include <string.h>
#include <iostream>

namespace {
struct Callback {
  virtual void* call() = 0;
};

class ThreadWrapper {
  pthread_t thread_;

 public:
  ThreadWrapper(void* (*start_routine)(void*), void* arg) {
    if (-1 == pthread_create(&thread_, nullptr, start_routine, arg)) {
      throw errno;
    }
  }
  ~ThreadWrapper() {
    if (-1 == pthread_join(thread_, nullptr)) {
      std::cerr << "Join failed!\n";
    }
  }
};

void* ThreadBody(void* arg) { return static_cast<Callback*>(arg)->call(); }
}  // namespace

int main() {
  struct Callback1 : public Callback {
    void* call() override {
      std::cout << "I'm Callback 1!\n";
      return nullptr;
    }
  } callback1;
  struct Callback2 : public Callback {
    void* call() override {
      std::cout << "I'm Callback 2!\n";
      return nullptr;
    }
  } callback2;

  try {
    ThreadWrapper t1(ThreadBody, &callback1);
    ThreadWrapper t2(ThreadBody, &callback2);
  } catch (int e) {
    std::cerr << strerror(e) << std::endl;
  }
}

To^^^ pochopitelně vyžaduje -lpthread.

(Jasně, používat takhle v C++ pthread je nesmysl, protože C++ má už dávno (od dob Rapperswilu) na vlákna vhodnější abstrakce, nicméně tohle je míněno jako příklad na "callback mezi C a C++".)

14.11.2019 14:17 Kojot
Rozbalit Rozbalit vše Re: C++ callback
Odpovědět | | Sbalit | Link | Blokovat | Admin
std::function
17.11.2019 16:52 Jardík
Rozbalit Rozbalit vše Re: C++ callback
Odpovědět | | Sbalit | Link | Blokovat | Admin
V čem je vlastně problém? V epoll_data můžeš mít vlastní pointer, to ti stačí. Můžeš si dynamicky naalokovat klidně std::function, kde budeš mít cokoliv, pointer předáš při přidávání deskriptoru v epoll_event::data, po volání epoll_wait tento pointer dostaneš, přetypuješ na pointer na std::function, dereferencuješ a zavoláš. Nebo místo alokace std::function můžeš naalokovat strukturu, kde bude ukazatel na objekt a ukazatel na funkci a pak funkci zavoláš na ten objekt.
22.11.2019 20:58 __blr__
Rozbalit Rozbalit vše Re: C++ callback
Odpovědět | | Sbalit | Link | Blokovat | Admin
Vyřešeno, díky moc!

Založit nové vláknoNahoru

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.