C callback fügvény ráhúzása C++ class tagfüggvényre

 ( zamek | 2011. szeptember 27., kedd - 15:06 )

hello,

Egy C-ben készült static library-ben definiált u_int (*func)(void) callback-re szeretném ráhúzni egy osztály u_int func(void) tagfüggvényét.
Reklamál a fordító, hogy nem egyezik a típus, valóban igaza lehet, mivel ez egy osztály tagfüggvénye.
Statikus osztályfüggvényre rá tudom húzni, de tagfüggvényre nem.

Hogy szokták ezt megoldani?

Tehát a c deklaráció a lib header-ben:

#ifdef __cplusplus
namespace tr_control {
	extern "C" {
#endif
...
typedef void write_out_func_t();
...
/**
    Set the display function callback.
    \param func entry point of display function
*/
void ctl_set_refresh_disp(write_out_func_t func);
...
#ifdef __cplusplus
	}
}
using namespace tr_control;
#endif

A C++ osztály header:

class A {
     ...
    static void refreshDisplay(void);
     ...
};

A konstruktorban:

A{
  ...
     tr_control::ctl_set_refresh_disp(&A::refreshDisplay);
}

Ez így működik, csak mivel a refeshDisplay() statikus, nem látja a példányváltozókat és függvényeket.

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

>>olvasnivalo

----------------------
"ONE OF THESE DAYS I'M GOING TO CUT YOU INTO LITTLE PIECES!!!$E$%#$#%^*^"

Koszonom, ez jo lett, bar a globalis valtozoban tarolt object nem tul elegens, de hat a C/C++ keveredes ilyen kodokat szul.

Igen, a fordítónak igaza is van, hiszen egy objektum tagfüggvényének meghívásához szükséged van a példány mutatójára. Ezt meg lehet úgy oldani, hogy a Singleton pattern-t használod (és nem globális változót :-)), ami saját maga manageli a saját egyetlen példányát. A ctl_set_refresh_disp függvényed marad statikus, azt regisztrálod be mint callback függvény, abból meg az statikus Instance() hívásával megkaphatod az ojjektumodra a mutatót (vagy referenciát, ahogy tetszik).

-------------------------------------------------
http://merlin3d.wordpress.com

Ez mar jobban tetszik. :)
koszonom az otletet.

Szívesen. :-)
-------------------------------------------------
http://merlin3d.wordpress.com

Azert mielott nagyon lelkendezni kezdenel, keress ra az adott oldalon a Meyers nevre es olvasd el az ott belinkelt doksit. Egyebkent nem art a tobbi referenciat is atbongeszni, mar a cimuk is arulkodo ;)

csak kérdezem, hogy szerinted a singletonnak milyen előnyei vannak a globális változóhoz képest, azon kívül, hogy szebb?

Hát ha engem kérdezel, szerintem több is van neki.

A példányhoz való hozzáférésed ezzel a mintával jól kontrollált, pontosan egy hely van az egész kódban, ahol ez a példány létrejön, sehol máshol nem lehet módosítani.

Kötheted a létrehozást bizonyos feltételekhez (pl. valamilyen állapot vagy jogosultság), amit a használónak teljesítenie kell.

A létrehozást delegálhatod egy gyártófüggvénynek vagy gyártóosztály példánynak. Ha gyártófüggvényed/objektumod van, akkor akár futási időben is eldöntheted, hogy a konrkét létrehozott típus mi legyen, az Instance által visszaadott példány típusának csak egy minimális interfészt kell teljesítenie, de bármi lehet ami ezzel kompatibilis. (Ezzel a létrehozott konrkét típust teljesen el is tudod rejteni a használótól.)

A későbbiekben egyszerűen át tudsz térni per-request példányosításra, ha pl. minden kérést külön objektumban akarsz lekezelni (pl. saját szálas végrehajtással vagy sorbaállítással).

Most hirtelen ennyi jutott eszembe, de a könyvben elég jól le vannak ezek írva. Persze jó kérdés, hogy ezek az előnyök ebben a konkrét feladatban kihasználhatók lesznek-e valaha... :-)
-------------------------------------------------
http://merlin3d.wordpress.com

köszi :) nem ezzel a feladattal kapcsolatban kérdeztem, csak úgy általánosságban.

inkabb csak hatranyai vannak

--
NetBSD - Simplicity is prerequisite for reliability

Neha kell, de nehez ugy.

Ha van egy eroforrasod, amibol csak egy van, akkor nem tudod maskepp megcsinalni, vagy hazudos lesz az interface-ed. Szerintem tisztabb egy singleton, mint egy egy olyan mutato, amit mindenki tovabbad, de valojaban globalis...

Cserebe a globalis valtozoknal minden jobb, szoval singelton kell.

cin, cout

--
NetBSD - Simplicity is prerequisite for reliability

Mikor is inicializalodik a cin, cout?

Random időpontban a main előtt?
----
India delenda est.
Hülye pelikán

Pontosan ez az.

És mivel a cin, cout, clog, cerr olyan objektumok, amik nem függnek semmitől, ez önmagában csak akkor probléma, ha a main előtt szeretnél loggolni. De akkor meg ott a printf :) Amúgy igazad van, a globális változók szar dolgok.
----
India delenda est.
Hülye pelikán

Ha megengeded a globalis valtozokat, elobb utobb valaki letrehoz majd egy objektumot, ami a cout-ot hasznalja a main elott :-)))
Csak ido kerdese.

Sőt, valószínűleg már megtörtént. Amúgy a globális változó nem mindig az ördög találmánya, van, hogy egyszerűbb azzal, csak körültekintéssel kell csinálni.
----
India delenda est.
Hülye pelikán

Ja, mint a goto, a tobbszoros ciklus, a kodismetles vagy a 100 sornal hosszabb fuggvenyek.

Altalaban rossz, neha, marginalis esetben jo.

persze, minden nap ilyet csinalok!

kivancsi lennek a kodra, ami ezt megcsinalja, de tenyleg!

--
NetBSD - Simplicity is prerequisite for reliability

Azt nem tudod elképzelni, hogy main előtt inicializálódó változók egymásra hivatkoznak? Pedig előfordul.
----
India delenda est.
Hülye pelikán

ELF-nel a .ctor sectionbol futtatja le ld.so vagy a crt, miert?

--
NetBSD - Simplicity is prerequisite for reliability

Vagyis nem tudod befolyasolni az inicializalodasi sorrendet.
Szoval a cin mint globalis valtozo szerintem tervezesi hiba, vagy legalabbis kompromisszum.

Ha pedig dinamikusan inicializalsz, akkor mar legyen singleton. Legyen mutator, accessor, kontrolalt eleres. Keves budosebb dolog van, mint globalis valtozok utan szart lapatolni.

es egy singleton miben mas, mint a globalis valtozo, attol eltekintve, hogy egy csomo limitacioja, ami legtobbszor nem is indokolt?

--
NetBSD - Simplicity is prerequisite for reliability

Mondjuk a singleton akkor jön létre, amikor először meghívják a getinstance-át, nem valami random időpontban, tehát amikor szükség van rá, garantáltan létezni fog.
----
India delenda est.
Hülye pelikán

Stop, erre nincs garancia. A getInstance() -nak az a szerzodese, hogy visszaadja az egyetlen peldanyt. De hogy ez mikor jon letre, arra vonatkozoan nem mond semmit, ez mar belemagyarazas. Siman lehet, hogy van egy kulon, kotelezoen meghivando inicializalo fuggvenye is (mert, mittudomen, egy factory-nak at kell adnia a konfiguraciot is hozza -, es az gyartja le neked az instance-t.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

Lehet, hogy a tankönyvi pattern szerint nincs rá garancia, mi nem arról beszéltünk.
----
India delenda est.
Hülye pelikán

Altalaban sincs erre garancia. Mondok peldat is: adatbazis kapcsolat. Meg ha nincs is poolozva, akkor is, a getInstance az esetek 99%-aban parameter nelkuli, vagyis valahol masutt kell incializalni. Nem csak a tankonyvben, azon kivul is.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

OOP? Accessorok, mutatorok? Jól definiált scope?
Thread-safety?
Kontrollált létrehozás?

Kiváncsi lennék, hogy milyen hátránya van egy singletonnak a globális változóval szemben?

Pont a thread-safe működés megvalósításához alkalmaztam singleton osztállyal burkolt globális változót.
Igaz, ez SystemC volt, ami egy különös jószág.

OOP-t miben serti az egyik, miben nem a masik?

accessor, mutator, karburator?

varom a peldakkal illusztralt magyarazotot a fent emlitett szakkifejezesekre es, hogy miben kulonbozik ezek tekinteteben a
Logger::getInstance()->log() vs Logger::logger.log(...)

kontrollalt letrehozas meg pont, hogy nincs mert elso hasznalatkor fog letrejonni es nem meghatarozott idoben
esetleg a lazy initalizationre gondoltal, ami nyilvan mindennapos kovetelmeny (nem)
mellesleg mikor semmisul meg a singleton? :)

hatranyokrol pedig itt olvashatsz (pontosabban a szuksegtelen "elonyokrol"):

http://jalf.dk/blog/2010/03/singletons-solving-problems-you-didnt-know-you-never-had-since-1995/

--
NetBSD - Simplicity is prerequisite for reliability

Természetesen a singleton rossz. Tisztességes ember nem használja. De ettől függetlenül a globális változóhoz képest csak előnye van. A lazy inicializálás szerinted nem kontrollált? Hiszen pontosan akkor jön létre, amikor először hivatkozom rá.
----
India delenda est.
Hülye pelikán

+1.

Plusz tudsz irni neki create() fv-t. Jol kontrollalt lesz a a letrehozasa.

"Some­times, we do need glob­als, yes. In those cases, make old-fashioned glob­als. Use sta­tic class mem­bers, or if the lan­guage allows it, global (non-member) objects. "

Ezzel nem ertek egyet. Pont a forditottjat gondolom. Nem gyozott meg, hogy miert hasznalnek globalis valtozot singleton helyett.

A singletonnak megvan az az elonye, hogy rogotn az interface-bol latod, hogy itt static memberek vannak, vagy globalis valtozok. Szerintem keves ocsmanyabb dolog van, mint egy osztalyba elrejtett static member.

akkor meg utoljara annyit kerdeznek, hogy akkor miert nem singletonnal csinal console apit sem a c++ es java sem a c# es te hogyan csinalnad

--
NetBSD - Simplicity is prerequisite for reliability

Nem tudom, utananezek.

Mert egyszerűbb használni:)
Olyankor van probléma, amikor két fordítási egységben lennének globális objektumok, és az egyik hivatkozza a másikat az inicializálásakor. Ilyenkor vagy egy TU-ba kell rakni, vagy singleton.

Raadasul a c++ std sok olyasmit csinal, amit nem szoktunk. Pl. a kontainerek destruktorai nem virtualisak. C| es java nyelvekben nem vagyok otthon, ott nem tudom.

Amit szabad Jupiternek...

Amugy ezek a szabalyok ugyis csak okolszabalyok. Nem hasznalunk goto-t. Nincs globalis valtozo. Neha vannak, de NAGYON ritkan indokoltak.

Szerintem ritkan indokolt a globalis valtozo, akkor mar inkabb a singleton...

"Pl. a kontainerek destruktorai nem virtualisak."

Tehát minden osztályodnak virtuális a destruktora? Mert akkor te nem C++-ozol, hanem valami Java/C# stílust próbálsz a nyelvre erőltetni.
Akkor leírom neked: a nem virtuális (ejtsd: nem öröklésre szánt) osztály teljesen jó és elfogadható és valid.
----
India delenda est.
Hülye pelikán

Nem igazán tetszik ennek a hozzászólásnak a hangneme. Ettől eltekintve:

"Tehát minden osztályodnak virtuális a destruktora?"

Nem, csak amelyikből van öröklés. Amúgy általában van a nem POD osztályoknak.
Amúgy pl. a Misra (ami néha elő van írva nekünk) meg is követeli.

Akkor nincs gond, mert konténerből nem öröklünk.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

System.out.println()-re gondolsz, az nem egy túl szép megoldás, viszont
gondolom sokszor kellett leírniuk, és még nem igazán voltak loggerek.
Aki ma System.out-ot használ, annak le kell törni a kezét...

Igy hirtelen nem is jut az eszembe más hasonló megoldás, viszont azért
nem egy nagy globális változó van bedobva az éterbe, hanem osztályba
van csomagolva:

public final class System{

public static PrintStream out;

}

Általánosan azt mondanám, hogy a globális változókkal addig nincs feltétlenül
nagy gond, amíg azok egy jól meghatározott osztályba vagy osztálycsoportba vannak
berakva. Amikor viszont tetszőleges osztályokba csinálnak globális változókat,
és ezeket keresztül-kasul próbálják elérni, az a nagyon nem kéne kategória...

- Aki ma System.out-ot használ, annak le kell törni a kezét...
+ Aki ma logolásra System.out-ot használ, annak le kell törni a kezét...

Csak mert leteznek konzolos appok is.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

nyilván így értettem... +1 :)

Apro kiegeszites: .NET-ben a System.Console egy osztaly, annak statikus metodusai vannak es maga a System.Console.Out egy static property, szoval a mogottes implementacio barmi lehet. (De most nem erek ra megnezni, hogy mi, bar valoszinuleg hasonlo, mint mondjuk egy std::out vagy egy System.out inicializalasa.

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

Pl. ha azt akarod, hogy az objektumod állapotát ne tudja bárki befolyásolni. Ekkor egy const globális változó nem nyerő, mert annak az állapotát senki nem tudja befolyásolni. Azonban ha singletont alkalmazol, a getinstance egy const referenciát ad vissza, így kívülről nem befolyásolható az objektum állapota, de az osztályon belülről igen.

Const referencián csak const függvényeket lehet meghívni, tehát ugyanott vagy, mint ha const objected lenne.
----
India delenda est.
Hülye pelikán

Látom nem jött át, amit mondani akartam. Pont ez a lényeg. :) Viszont az osztályon belül már nem const az objektum, így onnan meghívhatóak a nem const metódusok is.

Osztályon belül már nem const? Hacsak nincs neki saját szálja vagy ilyesmi, tehát nem aktor ő maga, akkor a constból nem fogsz tudni kitörni. Const függvény this pointere const, azon csak const függvényt hívhatsz.
----
India delenda est.
Hülye pelikán

Ha egy const cuccot egy nem const valtozonak adsz ertekul, es nem kapsz hatalmas warningokat ra, akkor rossz a forditod. Ezt meg en is tudom, pedig - tobben tanusithatjak - C++-hoz abszolut nem ertek.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

http://pastebin.com/mbJEFJCt

Tudom erőltetett a példa, de nagyjából erre gondoltam.

btw singletont pont nem így szokás csinálni, hanem

static T getInstance() { 
  static T t; 
  return t; 
}

Igy biztos nem, de tudjuk be a kesoi vagy korai oranak ;)

eh, class T { public: static T& getInstance() { static T t; return t; } ... };, na:)

meg mindig nem thread safe

--
NetBSD - Simplicity is prerequisite for reliability

haggyálmár:)

dehat a singletonnak semmi hatranya a globalishoz kepest es teljesen determinisztikus a letrehozas!

--
NetBSD - Simplicity is prerequisite for reliability

nekem teljesen mindegy, hogy ki mit használ, ne nekem mondd!

Az h. vki nem tudja leprogramozni meg nem hatrany.

Tényleg, a függvény static változók inicializálását lehet valahogy atomivá tenni? Nyílván nincs rá platformfüggetlen módszer, de valami gcc hekk?
----
India delenda est.
Hülye pelikán

GCC 4-tol ez mar megoldott (guard), persze csak ha nem hasznalod a -fno-threadsafe-statics kapcsolot. Viszont igy lesz egy kevesbe portolhato kodod, ami nem feltetlenul hasznos ;)

De nem is feltétlenül hátrány:)
Szóval akkor megoldott, hogy a függvény-statikus változó létrejötte, és a létrejöttének a feljegyzése kritikus szakaszként zajlik le, és aki belép a függvényvbe az vagy azt látja, hogy még nem létezik, és akkor tényleg ő az első, aki ráfutott, és ő fogja lekreálni, vagy azt, hogy már létezik, és tényleg?
----
India delenda est.
Hülye pelikán

Mi tart vissza, hogy kiprobald? Irsz egy singletont, a konstruktoraba beraksz egy kis kesleltetest es meghivod a getInstance() (vagy ahogy hivjak) fuggvenyet ket vagy tobb threadbol. Az elso thread ki fogja fekezni a tobbit, egesz addig amig a konstruktor vissza nem ter. Ez egyben meg is mutatja, milyen problemakat vethet fel egy singleton, ha a konstruktora idoigenyes muveleteket vegez. Persze az inicializalast at lehet rakni egy initfuggvenybe, viszont ekkor a tobbszoros hivas lehetoseget ki kell vedeni. Ha kiprobalod, rogton meg is nezheted, mi tortenik, ha a programodat a -fno-threadsafe-statics opcioval forditod. A kulonbseg mar az assembly kodban is latszik, a masodik esetben hianyozni fognak a __cxa_guard sorok.
De megegyszer mondom, ha nem tudod, milyen forditoval es milyen opciokkal fordul majd az altalad irt forraskod, nem egy okos gondolat a fentiekben megbizni.

Meg kell nezni C++11 hatha ad vmit, nem tudom.

Nyelvi eszköz szinte biztos, hogy nincs erre, de majd megnézem mégegyszer a multithread dolgokat.
----
India delenda est.
Hülye pelikán

C++11 esetén egész sok újdonság jött MT területen:

http://en.cppreference.com/w/cpp/thread

(Bár úgy is fogalmazhatnánk, hogy már végre van az STL-ben rá valami és nem szükségszerű 3rd party lib rá :)

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

http://en.cppreference.com/w/cpp/thread/call_once

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

... na, szoval igy sem.
--
Ki oda vagyik, hol szall a galamb, elszalasztja a kincset itt alant. | Gentoo Portal

§27.3/2: "The objects [std::cin, std::cout, etc.] are constructed, and the associations are established at some time prior to or during first time an object of class ios_base::Init is constructed, and in any case before the body of main begins execution."

Footnote: "Constructors and destructors for static objects can access these objects to read input from stdin or write output to stdout or stderr."

Szóval ott nincs gond, de ez csak annyit jelent, hogy a cin, cout, cerr rossz példa.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

Ugye nincs túl későn hozzászólni egy év után?
Az igazi természetesen az lenne, ha a programozót meg lehetne kérni, hogy legyen plusz egy paramétere a 'ctl_set_refresh_disp'-nek, amit azután továbbpasszol a 'func'-nak. Teljesen véletlenül van is erről egy példaprogramom: cback.cc

Nyílván az igazi ez lenne, de a meglévő C-s interfészek gyakran nem módosíthatóak, meg ha csak egy ilyen callback kell, akkor lényegtelen, hogy az interfészre kerül be ez a hekk, vagy a kliensoldalra.
----
India delenda est.
Hülye pelikán

Sajnos igaz. Pl itt van rögtön a qsort meg a bsearch. Sokszor praktikus lenne, ha a callback fv-nek lehetne paramétere, ehelyett globális változóval kell megoldani.

Vagy STL bind-et használsz. Ha megteheted.

Az a baj, hogy az ilyen userData pointer nélküli callback-ek már C-ben is gondot okoznak, mert az adott lib-et nem tudod több "példányban" használni. Pl ffmpeg-ben elvileg tudsz egy process-ből több szálon több videót dekódolni (ha regisztrálsz lock-release callback-et egy globális mutexnek), de semmi módon nem tudod kideríteni, hogy az error callback ezek közül mégis melyikből jött...

Szóval egy ilyen callback már C ben is fail, kivéve ha garantált, hogy nem használhatod a C-s libet több "példányban".
Hajlok arra, hogy az eredeti kérdésben felvetett eset ilyen, de ilyenkor nem látom akadályát annak, hogy valaki statikus fv-t regisztráljon...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

ffmpeg-ben elvileg tudsz egy process-ből több szálon több videót dekódolni [...] de semmi módon nem tudod kideríteni, hogy az error callback ezek közül mégis melyikből jött

Eléggé a levegőbe beszélek, de azért létezik pthread_getspecific() illetve pthread_self()/pthread_equal().

(Szerkesztés: továbbá ha nem fintorgunk a gcc kiterjesztésein, akkor van __thread is.)

Van, kivéve, hogy az ffmpeg belül szintén multithread, így nem garantált, hogy az error abból a thread-ből jön ahol a processing-et indítottad...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee." -- Ted Ts'o

"Statikus osztályfüggvényre rá tudom húzni, de tagfüggvényre nem. "

Ugyan már enyhén offtopic, de egy OOP-t támogató nyelvben ez miért nem magától értetődő? :)

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

Hogy egy OOP-t nem támogató nyelvvel való érintkezés során felmerülnek kérdések?
----
India delenda est.
Hülye pelikán

Nem azert, de szerintem aki picit is tisztaban van az OOP-vel es a mukodesevel, abban nem merul fel olyan, hogy tagfuggvenyt statikusan hivni.

Ertem en, hogy PHP-ben ezt meg lehet jatszani, de a topic mintha C++ lenne :)

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

Akkor tisztázzuk: a topik arról szólt, hogy egy C-s lib (C: procedurális nyelv, annak is alacsonyszintű) kíván egy olyan függvényt, amit meghívhat. Hol jött itt az OOP? C-s libet használsz, ne kérd rajta számon, hogy miért akar nem-tag függvényt hívni.
----
India delenda est.
Hülye pelikán

"szeretném ráhúzni egy osztály u_int func(void) tagfüggvényét. "

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™

Egy C-s interfészre. Mit nem értesz ezen?
----
India delenda est.
Hülye pelikán

Azt nem ertem, te mit nem ertesz ezen. Nem statikus metodust megis mifele logika menten kellene, hogy hivhato legyen a vakvilagbol ugy, hogy nem egy objektumpeldanyon hivod? (Az, hogy C-bol, az itt total irrelevans.)

(Bar ez fennt is ki lett targyalva.)

----------------
Lvl86 Troll, "hobbifejlesztő" - Think Wishfully™