Volání funkce z jiné třídy ukazatelem typu obecné funkce – C / C++ – Fórum – Programujte.com
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Volání funkce z jiné třídy ukazatelem typu obecné funkce – C / C++ – Fórum – Programujte.comVolání funkce z jiné třídy ukazatelem typu obecné funkce – C / C++ – Fórum – Programujte.com

 

pretis
~ Anonymní uživatel
6 příspěvků
12. 12. 2012   #1
-
0
-

Ahoj, píšu si EventDispatcher takový jaký má ActionScript3 a narazil jsem na problém. Náznak funkčnosti: dispatcher funguje tak, že třídy které ho využívají do něj přidají Listener, ten obsahuje stringrovou proměnnou názvu eventy a odkaz("handler") na svoji funkci u nich ve třídě. Dispatcher potom při přijetí eventy odjinud projde všechny listenery a podle názvu eventy volá handlery v listenerech. Všechno funguje v mainu, ale problém je, že když jsou handlery ve třídách, EventDispatcher při vytvoření listeneru nerozezná jednotlivé typy funkcí protože chodí každá od jiné třídy. Dá se nějak udělat aby měl dispatcher typdef handleru nějakou obecnou hodnotu?

takhle to vypadá:

typedef void(*EventHandler)(Event*);

při kompilaci vyhodí chybu 

no matching function for call to 'Scene::addEventListener(const string&, void (Game::*)(GameEvents::Event*))'

protože typ handleru je jen *EventHandler ne Game::*EventHandler ale když mám x tříd tak to potřebuji obecně.

Dá se to tedy nějak rozumně udělat? Mám už sice vymyšlenou jednu metodu jak to udělat ale přidává další třídu a je tam potřeba vyšší režie ze strany tříd které využívají dispatcher takže bych se bez této metody rád obešel.

díky moc za odpovědi, zdraví pretis.

Nahlásit jako SPAM
IP: 78.80.61.–
ingiraxo+15
Grafoman
12. 12. 2012   #2
-
0
-

noo já nevim jestli je tohle přesně co hledáš, ale napadlo mě třeba toto (psal jsem to z hlavy, tak tam snad nebude chyba)... jinak jsem se nechal inspirovat Javou (takto to funguje u ni)



#include <vector>
#include <stack>

// rozhrani s udalostmi zasobniku
class StackListener
{
public:
    virtual void Push(int i) = 0;
    virtual void Pop(int i) = 0;
};

// hlavni handler udalosti zasobniku
class StackEventHandler
{
public:
    void add(StackListener* listener)
    {
        listeners.push_back(listener);
    }

    void firePush(int i)
    {
        for (StackListener* listener : listeners)
            listener->Push(i);
    }

    void firePop(int i)
    {
        for (StackListener* listener : listeners)
            listener->Pop(i);
    }

private:
    vector<StackListener*> listeners;
};

// nejaka trida, ktera pracuje se zasobnikem
class MyStack
{
public:
    void addStackListener(StackListener* listener)
    {
        handler.add(listener);
    }

    void run()
    {
        for (int i = 0; i < 10; ++i)
        {
            stack.push(i);
            handler.firePush(i);
        }
        for (int i = 0; i < 10; ++i)
        {
            int top = stack.top();
            stack.pop();
            handler.firePop(top);
        }
    }

private:
    StackEventHandler handler;
    stack<int> stack;
};

// moje trida, ktera pouziva tridu se zasobnikem
class AnotherClass : public StackListener
{
public:
    AnotherClass()
    {
        stack.addStackListener(this);
        stack.run();
    }

    void Push(int i)
    {
        // pokud se do zasobniku prida prvek
    }

    void Pop(int i) // "top" prvek
    {
        // pokud se ze zasobniku odebere prvek
    }

private:
    MyStack stack;
};

je to sice delsi, ale snad pochopitelnejsi :)

Nahlásit jako SPAM
IP: 213.168.183.–
Moje aplikace: http://ophite.cz
Tutoriály na: C#
pretis
~ Anonymní uživatel
6 příspěvků
12. 12. 2012   #3
-
0
-

#2 ingiraxo
Ahoj, díky za odpověď, to se v podstatě se to podobá řešení na kterém teď dělám, ale pořád to neřeší můj problém. Jde mi o to abych v jedné třídě odeslal eventu a ve druhé ji odchytil a tím zavolal funkci která je na to navázaná(nadeklarovaná a naimplementovaná je ve třídě která eventu odchytila), to vše pomocí EventDispatcheru (který je singleton). Problém je ovšem v tom, že ty funkce volá EventDispatcher po odeslání eventy a ten už zase nezná jinej typ než 

typedef void(*EventHandler)(Event*);

kdeždo když si tam nějaká třída nabinduje svoje funkce tak to už je rozdílný typ, o to mě jde, nějak to sjednotit aby EventDiaspatcheru nezáleželo na tom z jaké třídy je ta funkce kterou volá.

Nahlásit jako SPAM
IP: 78.80.61.–
ingiraxo+15
Grafoman
12. 12. 2012   #4
-
0
-

   aha už asi vim co myslíš.. to můžeš udělat pomocí "kámoše" (friend)



typedef void (*EventHandler)(int n);

class A
{
public:
    A()
    {
        handler = nullptr;
    }

    void setHandler(EventHandler handler)
    {
        this->handler = handler;
    }

    void callEvent(int n)
    {
        if (handler != nullptr)
            handler(n);
    }

private:
    EventHandler handler;
};

class B
{
public:
    B()
    {
        a.setHandler(catchEvent); // nastavi funkci catchEvent na fcePtr v instanci (A::*)
        a.callEvent(100); // zavola funkci catchEvent z instance (A::*)
    }

private:
    A a;

    friend void catchEvent(int n)
    {
        // vykona se event
    }
};
Nahlásit jako SPAM
IP: 213.168.183.–
Moje aplikace: http://ophite.cz
Tutoriály na: C#
KIIV
~ Moderátor
+43
God of flame
12. 12. 2012   #5
-
0
-

stejne je handler odvozenej z nejaky tridy vcelku k nicemu (pokud neni staticky), bez predani instance tridy ke ktere patri - formalne je prave trida skryty parametr tech metod...

mozna pouzit neco jako boost::bind ... kde se snad da i predat reference na metodu, kterou chces pouzit, tridu ke ktere to patri a pak placeholder pro parametr

Nahlásit jako SPAM
IP: 62.168.56.–
Program vždy dělá to co naprogramujete, ne to co chcete...
vitamin+8
Grafoman
12. 12. 2012   #6
-
0
-

#3 pretis

Treba si uvedomit ze metody maju este jeden parameter a to je this. Nomzes urobit pointer ktori dokaze ukazovat na staticku funkciu a zaroven moze ukazovat na metodu lebo maju ine parametre.

Nahlásit jako SPAM
IP: 95.105.157.–
obfuscate: "The cruel god Malloc will strike you down. "
ZMeson: "That's the C god. C++ has a new god. "
pretis
~ Anonymní uživatel
6 příspěvků
12. 12. 2012   #7
-
0
-

Už tomu rozumím, předám tam funkci která tam bude mít this a ukazatel na tu eventu, a dispatcher potom zavolá tu konkrétní funkci pomocí toho co je v this(čili ta třída, která vlastní onu funkci) je to tak? Jak má potom ale vypadat definice toho EventHandler? Jinak s tím kamarádem se mi to líbí ale kompilátor mě vyhodí 

error: 'catchEvent' is not a member of 'B'

když oddělám friend ta to jde ale zase to hlásí stejnou chybu že nejsou stejné ty typy funkcí. S friend jsem ještě nedělal jak tu chybu opravit?

Nahlásit jako SPAM
IP: 78.80.61.–
ingiraxo+15
Grafoman
12. 12. 2012   #8
-
0
-

#7 pretis
to co jsem napsal funguje bez problemu (zkousel jsem), jinak friend tam musi byt, umoznuje ti aby privatni handler v A volal funkci v B, aniz by dedil tridu A

a tu chybu ti to pise, ze funkce "catchEvent" neni ve tride B

Nahlásit jako SPAM
IP: 213.168.183.–
Moje aplikace: http://ophite.cz
Tutoriály na: C#
pretis
~ Anonymní uživatel
6 příspěvků
12. 12. 2012   #9
-
0
-

#8 ingiraxo
Ja to chápu jak to má fungovat, ale nevím proč mě to píše tu chybu, kód je přímo zkopírovaný. Navíc ztratím možnost pracovat s atributy tridy B nebo ne?

Nahlásit jako SPAM
IP: 78.80.61.–
ingiraxo+15
Grafoman
12. 12. 2012   #10
-
0
-

#9 pretis
proč by si ztratil možnost? vsak tady jde pouze o propojeni udalosti z tridy A do B

Nahlásit jako SPAM
IP: 213.168.183.–
Moje aplikace: http://ophite.cz
Tutoriály na: C#
pretis
~ Anonymní uživatel
6 příspěvků
12. 12. 2012   #11
-
0
-

#10 ingiraxo
protože jakmile není nějaký atribut statický, nemůžu k němu přistoupit

error: invalid use of non-static data member 'B::cislo'
Nahlásit jako SPAM
IP: 78.80.61.–
ingiraxo+15
Grafoman
12. 12. 2012   #12
-
0
-

#11 pretis
a proč je statickej? nemůžeš pristupovat z non-sattic na static.. spis sem dej jak to mas, protoze nejak nechapu o co se porad snazis... mozna je chyba, ze to mas jako singleton a ne normalni tridu

Nahlásit jako SPAM
IP: 213.168.183.–
Moje aplikace: http://ophite.cz
Tutoriály na: C#
vitamin+8
Grafoman
12. 12. 2012   #13
-
0
-

riesenie by mohlo vyzerat takto:

#include <iostream>
#include <vector>
#include <memory>
#include <string>
#include <functional>
using namespace std;


class EventHandler{
	public:
		virtual void operator()(int n)=0;
};

//EventHandler pre staticke funkcie
class fncEventHandler : public EventHandler{
	public:
		typedef  std::function<void(int)> fnc_t;
		//typedef void (*fnc_t)(int n);
	private:
		fnc_t fnc;	//pointer na funkciu
	public:
		fncEventHandler(fnc_t fnc):fnc(fnc){}
		void operator()(int n){return fnc(n);}
};

//EventHandler pre metody
template <class T>
class metEventHandler : public EventHandler{
	public:
		typedef void (T::*met_t)(int n);
	private:
		T* c;		//pointer na triedu (this metody met)
		
		met_t met;	//pointer na metodu
	public:
		metEventHandler(T* c, met_t met):c(c), met(met){}
		void operator()(int n){return (c->*met)(n);}
};


unique_ptr<EventHandler> CreateEventHandler(fncEventHandler::fnc_t f){
	return unique_ptr<EventHandler>(new fncEventHandler(f));
}

template <class T>
unique_ptr<EventHandler> CreateEventHandler(T& c, typename metEventHandler<T>::met_t met){
	return unique_ptr<EventHandler>(new metEventHandler<T>(&c, met));
}

//testovacia trieda
class A{
	public:
		void fnc(int n){cout << "A::fnc()\n"; }
		void fnc2(int n){cout << "A::fnc2()\n"; }
	
};

//testovacia funkica
void fnc(int n){cout << "::fnc()\n"; }

int main (int argc, char **argv){
	
	A a1, a2, a3;
	vector<unique_ptr<EventHandler>>	handlers;
	
	handlers.push_back(CreateEventHandler(a1, &A::fnc));
	handlers.push_back(CreateEventHandler(fnc));
	handlers.push_back(CreateEventHandler(a2, &A::fnc2));
	handlers.push_back(CreateEventHandler(a3, &A::fnc));
	handlers.push_back(CreateEventHandler([](int){cout << "lambda()\n";}));
	
	for(auto& a : handlers)	//zavola vsetky registrovane handlery s parametrom 4
		(*a)(4);	
	
	
}
Nahlásit jako SPAM
IP: 95.105.157.–
obfuscate: "The cruel god Malloc will strike you down. "
ZMeson: "That's the C god. C++ has a new god. "
pretis
~ Anonymní uživatel
6 příspěvků
12. 12. 2012   #14
-
0
-

#13 vitamin
Díky moc, tohle si myslím že už bych mohl použít:)

Nahlásit jako SPAM
IP: 78.80.61.–
ondra.holub+1
Stálý člen
13. 12. 2012   #15
-
0
-

#1 pretis
Obecně se callback v C dělá obvykle tak, že se zaregistruje nejenom ukazatel na funkci, kterou chceš zavolat, ale ještě k tomu ukazatel na nějaká user data. A tam si každý při registraci callbacku strčí cokoliv potřebuje. Ukazatel na user data je pak předán v jednom z parametrů do funkce callbacku. Ta funkce už ví, jaký typ tam má očekávat, takže si void* přetypuje na ukazatel na svůj typ a user data z něj nějak smysluplně využije.

V C++ se callback obvykle řeší tak, že se místo ukazatele na funkci zaregistruje ukazatel na instanci nějaké třídy, která je zděděná z abstraktního předka. Tento předek má virtuální metodu (obvykle čistě virtuální - pure), kterou je třeba v potomkovi předefinovat. A zavolání callbacku pak není nic jiného než zavolání této virtuální metody. User data zde nejsou nutná, protože zděděná třída si může dodefinovat jakákoliv vlastní data, kterými rozšíří data ve svém předkovi a má k nim přístup v té volané virtuální metodě.

Pokud máš C API (tedy registruješ ukazatel na funkci a ukazatel na user data) a chceš jej využívat z C++, můžeš si zaregistrovat ukazatel na statickou funkci a jako user data dát ukazatel na svoji C++ třídu reprezentující ten callback. Ta statická funkce pak jenom zavolá příslušnou metodu na správné instanci:

struct BaseCToCppCallback
{
    virtual void cpp_callback() = 0;

    static void c_callback(void* user_data)
    {
        BaseCToCppCallback* cpp_inst = (BaseCToCppCallback*)user_data;
        cpp_inst->cpp_callback();
    }
};

struct MyCppCallback: public BaseCToCppCallback
{
    virtual void cpp_callback()
    {
        std::cerr << "MyCppCallback called\n";
    }
};

...

// Zaregistrovani callbacku pres C-API
RegisterCallback(&BaseCToCppCallback::c_callback, new MyCppCallback);

User data nejsou absolutně nezbytně nutná, protože je lze strčit i do nějaké globální proměnné. Ale pak je to docela bastl, protože se k těm datům dá dostat i odjinud a hlavně pak musí být jedna callback funkce registrovaná jenom jednou. Někdy to může stačit, někdy ne. Každopádně je to dost omezující.

Nahlásit jako SPAM
IP: 194.138.12.–
vitamin+8
Grafoman
13. 12. 2012   #16
-
0
-

#15 ondra.holub
V c++ nemusis zahadzovat informacie o typoch pretypovani na void*. Radsej pouzi templaty. Okrem toho ze to bude typovo bezpecne, budeto sa to lepsie pouzivat a moze byt taky kod aj rychlejsi.

Nahlásit jako SPAM
IP: 95.105.157.–
obfuscate: "The cruel god Malloc will strike you down. "
ZMeson: "That's the C god. C++ has a new god. "
ondra.holub+1
Stálý člen
13. 12. 2012   #17
-
0
-

#16 vitamin
Jestliže registruji callback přes C API, tak mně templaty vůbec nepomůžou. V C prostě ta user data musí být jako nějaký ukazatel. Obvykle typu void*, protože se to stejně musí přetypovávat, tak není důvod budit dojem, že je v tom nějaký konkrétní typ. Typově to bude nebezpečné jenom na tom jediném místě, kde se C++ připojuje na C-API.

Nahlásit jako SPAM
IP: 194.138.12.–
vitamin+8
Grafoman
13. 12. 2012   #18
-
0
-

#17 ondra.holub
Uz som pochupil co chces docielit :)

Kedze si #pretis pise cely kod sam, tak snad nemusi pouziva c api.

Nahlásit jako SPAM
IP: 95.105.157.–
obfuscate: "The cruel god Malloc will strike you down. "
ZMeson: "That's the C god. C++ has a new god. "
Zjistit počet nových příspěvků

Přidej příspěvek

Toto téma je starší jak čtvrt roku – přidej svůj příspěvek jen tehdy, máš-li k tématu opravdu co říct!

Ano, opravdu chci reagovat → zobrazí formulář pro přidání příspěvku

×Vložení zdrojáku

×Vložení obrázku

Vložit URL obrázku Vybrat obrázek na disku
Vlož URL adresu obrázku:
Klikni a vyber obrázek z počítače:

×Vložení videa

Aktuálně jsou podporována videa ze serverů YouTube, Vimeo a Dailymotion.
×
 
Podporujeme Gravatara.
Zadej URL adresu Avatara (40 x 40 px) nebo emailovou adresu pro použití Gravatara.
Email nikam neukládáme, po získání Gravatara je zahozen.
-
Pravidla pro psaní příspěvků, používej diakritiku. ENTER pro nový odstavec, SHIFT + ENTER pro nový řádek.
Sledovat nové příspěvky (pouze pro přihlášené)
Sleduj vlákno a v případě přidání nového příspěvku o tom budeš vědět mezi prvními.
Reaguješ na příspěvek:

Uživatelé prohlížející si toto vlákno

Uživatelé on-line: 0 registrovaných, 40 hostů

Podobná vlákna

Qt slot zavolani funkce z jine tridy — založil rodinne.baleni.ryze

Volani metody z jine tridy — založil Sunshinek

Volání metody jiné třídy — založil Matěj_

Moderátoři diskuze

 

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý