/ Forside / Teknologi / Udvikling / C/C++ / Nyhedsindlæg
Login
Glemt dit kodeord?
Brugernavn

Kodeord


Reklame
Top 10 brugere
C/C++
#NavnPoint
BertelBra.. 2425
pmbruun 695
Master_of.. 501
jdjespers.. 500
kyllekylle 500
Bech_bb 500
scootergr.. 300
gibson 300
molokyle 287
10  strarup 270
Include / klasse / reference
Fra : Tornado


Dato : 01-05-01 10:05

Hej

Jeg har et problem i C++ (til Linux).

- Idéen:
At få 2 klasser til at "snakke" sammen - dvs at kalde funktioner hos
hinanden. Mange har spurgt mig hvad jeg skal bruge det til - så jeg vil da
sige det, og spare et par postings - det skal bruges til event/callback.

- Løsningsforslag:
(1) At bruge pointere til funktioner. Det har jeg prøvet med, og til sidst
opgivet det, da det skal være muligt at kalde funktioner i ukendte klasser
(dvs. klasser kan hedde forskelligt, dog er funktion(headeren) den samme).

(2) At lave en klasse pr event, også overføre referencen til klassen istedet
(hvilket betyder at klasserne skal nedarves fra en base/virtual klasse).
Selv om det er lidt skræmmende at have 1 klasse pr event, da der er tale om
MANGE events, så kan jeg ikke se andre udveje, da forslag (1) ikke virker.

- Problemet:
Jeg har 2 klasser, der hver har sin header (.h) og "body" fil (.cc). der
skal kunne referere til hinanden (kalde funktioner hos hinanden). Hvis jeg
inkluderer hinanden's headerfiler, klager den og fejler kompileringen.
Fjerner jeg derimod referencen, så den kun går den ene vej (lige meget
hvilken), så compiler den fint.

Fejlen, jeg får, når jeg inkluderer hinandens headerfiler er:
Kylling.cc.. In file included from Kylling.h:5,
from Kylling.cc:1:
Bamse.h:11: syntax error before `;'

Jeg compiler på følgende måde:
g++ Kylling.cc -o Kylling.o -c
g++ Bamse.cc -o Bamse.o -c

- Spørgsmålet:
Dem har jeg alt for mange af angående det her, så jeg vil istedet nøjes med
at stille 2 spørgsmål.

1. Hvordan får jeg løst det problem med at klasserne er i stand til at
referere til hinanden - dvs kan include hinandens headerfiler?

2. Pointere til funktioner virker ikke, hvis der er tale, at de skal kunne
kalde funktioner i klasser, hvis funtionsheaderen er den samme, men
klasse-typen/navnet er en anden - har jeg fået at vide, og også semi
bekræftet. Nogen der har nogle forslag til hvordan man kan? - Det vil være
mere optimalt end at have en klasse pr event.

Jeg har trimmet både header og 'body' filerne til de 2 klasser ned til det
mest nødvendige, dvs 8 liner (inklusiv inclusion guards) pr headerfil, og 1
line pr "body"-fil.

Jeg har tilladet mig at vedhæfte de 4 filer fremfor at copy/paste indholdet.
Hvis nogen ikke kan se vedhæftede filer, så vil jeg da gerne copy/paste
indholdet ind ;)

Mvh
- En naturkatastrofetingest

PS: Med "body" mener jeg faktisk implementeringen af headerne. Jeg kom først
i tanke om, hvad det rigtigt hed, og orkede ikke at rette det over alt ;)



begin 666 Kylling.h
M(VEF;F1E9B!?7TM93$Q)3D=?2 T*(V1E9FEN92!?7TM93$Q)3D=?2 T*#0H-
M"B-I;F-L=61E(")"86US92YH(@T*#0H-"F-L87-S($MY;&QI;F<-"GL-"@EP
M<FEV871E.@T*"0E"86US92!B.PT*?3L-"@T*#0HC96YD:68@+R\@7U]+64Q,
%24Y'7T@`
`
end

begin 666 Bamse.h
M(VEF;F1E9B!?7T)!35-%7T@-"B-D969I;F4@7U]"04U315](#0H-"@T*(VEN
M8VQU9&4@(DMY;&QI;F<N:"(-"@T*#0IC;&%S<R!"86US90T*>PT*"7!R:79A
M=&4Z#0H)"4MY;&QI;F<@:SL-"GT[#0H-"@T*(V5N9&EF("\O(%]?0D%-4T5?
#2 T*
`
end

begin 666 Kylling.cc
7(VEN8VQU9&4@(DMY;&QI;F<N:"(-#0H`
`
end

begin 666 Bamse.cc
8(VEN8VQU9&4@(D)A;7-E+F@B#0T*#0T*
`
end


 
 
Tornado (01-05-2001)
Kommentar
Fra : Tornado


Dato : 01-05-01 10:38

> Jeg har tilladet mig at vedhæfte de 4 filer fremfor at copy/paste
indholdet.
> Hvis nogen ikke kan se vedhæftede filer, så vil jeg da gerne copy/paste
> indholdet ind ;)

Jeg kan se at jeg er den eneste, der overhovedet har vedhæftet noget i denne
gruppe, så vil jeg da lige lave om - her er indholdet af de 4 (nedstippede)
filer:


// --------- Bamse.h ---------
#ifndef __BAMSE_H
#define __BAMSE_H

#include "Kylling.h"

class Bamse
{
private:
Kylling k;
};

#endif // __BAMSE_H



// --------- Bamse.cc ---------
#include "Bamse.h"



// --------- Kylling.h ---------
#ifndef __KYLLING_H
#define __KYLLING_H

#include "Bamse.h"

class Kylling
{
private:
Bamse b;
};

#endif // __KYLLING_H



// --------- Kylling.cc ---------
#include "Kylling.h"






bnj (01-05-2001)
Kommentar
Fra : bnj


Dato : 01-05-01 13:21

....vil du lade en Kylling indeholde en Bamse, som igen indeholder en
Kylling, der indeholder en Bamse... du skal nok være glad for at compileren
forhindrer det..

Desuden giver det ikke mening udfra dit ønske om at kunne udveksle
information mellem de to da den ene klasse jo altid vil være instantieret i
det andet og ikke som et selvstændigt objekt.. benyt istedet pointere.
Hvis du lader begge klasser arve fra en fælles klasse skulle det ikke være
noget problem at lave et interface med pointere at den fælles klasse.
f.eks:
// ClassTest.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <conio.h>

class dyr
{
public:

char m_name[80];

dyr(char *name)
{
strncpy(m_name,name,sizeof(m_name));
};

virtual ~dyr() {};

virtual void spis(dyr & d)
{
printf("%s spiser ikke andre dyr!\r\n", m_name );
};
};


class kaj : public dyr
{
public:
kaj():dyr("Frø"){};
virtual ~kaj(){};

virtual void spis(dyr & d)
{
printf("%s spiser en %s. Bøvs..\r\n", m_name , d.m_name);
};
};

class andrea: public dyr
{
public:
andrea():dyr("Papegøje") {};
virtual ~andrea(){};

virtual void spis(dyr & d)
{
printf("Andrea apiser en %s\r\n", d.m_name);
};
};


int main(int argc, char* argv[])
{
kaj k;
andrea a;
a.spis(k);
k.spis(a);
getch();
return 0;
}



"Tornado" <tornado@linuxfromscratch.org> wrote in message
news:9cm28g$106b$1@news.cybercity.dk...
> > Jeg har tilladet mig at vedhæfte de 4 filer fremfor at copy/paste
> indholdet.
> > Hvis nogen ikke kan se vedhæftede filer, så vil jeg da gerne copy/paste
> > indholdet ind ;)
>
> Jeg kan se at jeg er den eneste, der overhovedet har vedhæftet noget i
denne
> gruppe, så vil jeg da lige lave om - her er indholdet af de 4
(nedstippede)
> filer:
>
>
> // --------- Bamse.h ---------
> #ifndef __BAMSE_H
> #define __BAMSE_H
>
> #include "Kylling.h"
>
> class Bamse
> {
> private:
> Kylling k;
> };
>
> #endif // __BAMSE_H
>
>
>
> // --------- Bamse.cc ---------
> #include "Bamse.h"
>
>
>
> // --------- Kylling.h ---------
> #ifndef __KYLLING_H
> #define __KYLLING_H
>
> #include "Bamse.h"
>
> class Kylling
> {
> private:
> Bamse b;
> };
>
> #endif // __KYLLING_H
>
>
>
> // --------- Kylling.cc ---------
> #include "Kylling.h"
>
>
>
>
>





Tornado (01-05-2001)
Kommentar
Fra : Tornado


Dato : 01-05-01 14:14


"bnj" <bnj@sfktech.com> wrote in message
news:9cm9be$ee553$1@ID-73922.news.dfncis.de...
> ...vil du lade en Kylling indeholde en Bamse, som igen indeholder en
> Kylling, der indeholder en Bamse... du skal nok være glad for at
compileren
> forhindrer det..
>
> Desuden giver det ikke mening udfra dit ønske om at kunne udveksle
> information mellem de to da den ene klasse jo altid vil være instantieret
i
> det andet og ikke som et selvstændigt objekt.. benyt istedet pointere.
> Hvis du lader begge klasser arve fra en fælles klasse skulle det ikke være
> noget problem at lave et interface med pointere at den fælles klasse.
> f.eks:
> // ClassTest.cpp : Defines the entry point for the console application.
> //
>
> #include "stdafx.h"
> #include <stdio.h>
> #include <string.h>
> #include <conio.h>
>
> class dyr
> {
> public:
>
> char m_name[80];
>
> dyr(char *name)
> {
> strncpy(m_name,name,sizeof(m_name));
> };
>
> virtual ~dyr() {};
>
> virtual void spis(dyr & d)
> {
> printf("%s spiser ikke andre dyr!\r\n", m_name );
> };
> };
>
>
> class kaj : public dyr
> {
> public:
> kaj():dyr("Frø"){};
> virtual ~kaj(){};
>
> virtual void spis(dyr & d)
> {
> printf("%s spiser en %s. Bøvs..\r\n", m_name , d.m_name);
> };
> };
>
> class andrea: public dyr
> {
> public:
> andrea():dyr("Papegøje") {};
> virtual ~andrea(){};
>
> virtual void spis(dyr & d)
> {
> printf("Andrea apiser en %s\r\n", d.m_name);
> };
> };
>
>
> int main(int argc, char* argv[])
> {
> kaj k;
> andrea a;
> a.spis(k);
> k.spis(a);
> getch();
> return 0;
> }
>

Okay, jeg skulle måske ikke have trimmet det unødvendige væk - det har vist
vakt en masse misforståelser, ser det ud til. Jeg vil prøve at forklare det
lidt nærmere med ord istedet.

Objektet, der bliver instanseret af klassen Bamse, skal indeholde en
POINTER/REFERENCE til et objekt af klassen Kylling. Lige ledes skal
objekt-instansen af klassen Kylling, indeholde en POINTER/REFERENCE til
objektet Bamse.

Det skal de fordi de skal kunne kalde funktioner hos hinanden NÅR SOM HELST,
uafhængige af hinandens kald. Det nytter derfor ikke noget at overføre
referencen til det modsatte objekt, når der er behov for det (som
parameter). Referencen skal overføres vha constructoren hos den ene, og en
normal funktion hos den anden (da vi jo har 'hønen eller
ægget'-problemstillingen).

Hovedprogrammet sørger for at lave en ny instans af begge klasser, og sørger
for at kalde dem rigtig - noget ala følgende (speedy-gonsala-kode):

Kylling k = new Kylling();
Bamse b = new Bamse(&k);
k.setBamse(&b);

Derved kan bamsen, når den vil, kalde en funktion på kyllingen, og omvendt.

Det med nedarvning fra samme klasser dur ikke her, da de INTET har
tilfældes - ikke engang en funktion. I virkeligheden er de 2 klasser vidt
forskellige - hvilket er det, jeg har prøvet at illustrere ved at bruge
Bamse og Kylling - hvis man ikke har gættet det, så er det ikke mit rigtige
program, jeg har trimmet ned - det er et opdigtet program med SAMME
problemstilling - for at gøre det nemmere at forstå).

Jeg håber at det har kastet lidt lys på hvad jeg vil opnå.. :)

Mvh
- Moi




bnj (01-05-2001)
Kommentar
Fra : bnj


Dato : 01-05-01 15:09


Den eneste vej jeg kan se igennem "problemet", er at nedarve til klasser der
indeholder de indbydes referencer:

class bbamse
{
public:
int honning;
bbamse() {};
virtual ~bbamse() {};
};

class bkylling
{
public:
int orm;
bkylling() {};
virtual ~bkylling() {};
};

class bamse: public bbamse
{
public:
bkylling *min_ven;
};

class kylling : public bkylling
{
public:
bbamse *min_ven;
};

int main(int argc, char* argv[])
{
kylling k;
bamse b;

b.min_ven = &k;
k.min_ven = &b;

b.min_ven->orm = 1;
k.min_ven->honning = 0;

return 0;
}




"Tornado" <tornado@linuxfromscratch.org> wrote in message
news:9cmcsc$1gkm$1@news.cybercity.dk...
>
> "bnj" <bnj@sfktech.com> wrote in message
> news:9cm9be$ee553$1@ID-73922.news.dfncis.de...
> > ...vil du lade en Kylling indeholde en Bamse, som igen indeholder en
> > Kylling, der indeholder en Bamse... du skal nok være glad for at
> compileren
> > forhindrer det..
> >
> > Desuden giver det ikke mening udfra dit ønske om at kunne udveksle
> > information mellem de to da den ene klasse jo altid vil være
instantieret
> i
> > det andet og ikke som et selvstændigt objekt.. benyt istedet pointere.
> > Hvis du lader begge klasser arve fra en fælles klasse skulle det ikke
være
> > noget problem at lave et interface med pointere at den fælles klasse.
> > f.eks:
> > // ClassTest.cpp : Defines the entry point for the console application.
> > //
> >
> > #include "stdafx.h"
> > #include <stdio.h>
> > #include <string.h>
> > #include <conio.h>
> >
> > class dyr
> > {
> > public:
> >
> > char m_name[80];
> >
> > dyr(char *name)
> > {
> > strncpy(m_name,name,sizeof(m_name));
> > };
> >
> > virtual ~dyr() {};
> >
> > virtual void spis(dyr & d)
> > {
> > printf("%s spiser ikke andre dyr!\r\n", m_name );
> > };
> > };
> >
> >
> > class kaj : public dyr
> > {
> > public:
> > kaj():dyr("Frø"){};
> > virtual ~kaj(){};
> >
> > virtual void spis(dyr & d)
> > {
> > printf("%s spiser en %s. Bøvs..\r\n", m_name , d.m_name);
> > };
> > };
> >
> > class andrea: public dyr
> > {
> > public:
> > andrea():dyr("Papegøje") {};
> > virtual ~andrea(){};
> >
> > virtual void spis(dyr & d)
> > {
> > printf("Andrea apiser en %s\r\n", d.m_name);
> > };
> > };
> >
> >
> > int main(int argc, char* argv[])
> > {
> > kaj k;
> > andrea a;
> > a.spis(k);
> > k.spis(a);
> > getch();
> > return 0;
> > }
> >
>
> Okay, jeg skulle måske ikke have trimmet det unødvendige væk - det har
vist
> vakt en masse misforståelser, ser det ud til. Jeg vil prøve at forklare
det
> lidt nærmere med ord istedet.
>
> Objektet, der bliver instanseret af klassen Bamse, skal indeholde en
> POINTER/REFERENCE til et objekt af klassen Kylling. Lige ledes skal
> objekt-instansen af klassen Kylling, indeholde en POINTER/REFERENCE til
> objektet Bamse.
>
> Det skal de fordi de skal kunne kalde funktioner hos hinanden NÅR SOM
HELST,
> uafhængige af hinandens kald. Det nytter derfor ikke noget at overføre
> referencen til det modsatte objekt, når der er behov for det (som
> parameter). Referencen skal overføres vha constructoren hos den ene, og en
> normal funktion hos den anden (da vi jo har 'hønen eller
> ægget'-problemstillingen).
>
> Hovedprogrammet sørger for at lave en ny instans af begge klasser, og
sørger
> for at kalde dem rigtig - noget ala følgende (speedy-gonsala-kode):
>
> Kylling k = new Kylling();
> Bamse b = new Bamse(&k);
> k.setBamse(&b);
>
> Derved kan bamsen, når den vil, kalde en funktion på kyllingen, og
omvendt.
>
> Det med nedarvning fra samme klasser dur ikke her, da de INTET har
> tilfældes - ikke engang en funktion. I virkeligheden er de 2 klasser vidt
> forskellige - hvilket er det, jeg har prøvet at illustrere ved at bruge
> Bamse og Kylling - hvis man ikke har gættet det, så er det ikke mit
rigtige
> program, jeg har trimmet ned - det er et opdigtet program med SAMME
> problemstilling - for at gøre det nemmere at forstå).
>
> Jeg håber at det har kastet lidt lys på hvad jeg vil opnå.. :)
>
> Mvh
> - Moi
>
>
>



Rasmus Christensen (01-05-2001)
Kommentar
Fra : Rasmus Christensen


Dato : 01-05-01 22:43


Tornado <tornado@linuxfromscratch.org> skrev i en
nyhedsmeddelelse:9cmcsc$1gkm$1@news.cybercity.dk...
[...]
> Okay, jeg skulle måske ikke have trimmet det unødvendige væk - det har
vist
> vakt en masse misforståelser, ser det ud til. Jeg vil prøve at forklare
det
> lidt nærmere med ord istedet.
>
> Objektet, der bliver instanseret af klassen Bamse, skal indeholde en
> POINTER/REFERENCE til et objekt af klassen Kylling. Lige ledes skal
> objekt-instansen af klassen Kylling, indeholde en POINTER/REFERENCE til
> objektet Bamse.
[...]

Hvis det er tilfældet (at du bruger en pointer) behøver du ikke inkludere
den 'modsatte' header-fil i header-filen. Istedet kan du lavet en
forward-erklæring, og så inkludere header-filen, hvor du implementerer
klassen:

// Kylling.h

// Forward decl
class Bamse;

class Kylling
{
....
Bamse *m_pBrianNielsen;
....
};

---

// Kylling.cpp

#include "Bamse.h"
#include "Kylling.h"

Kylling::Kylling()
{
m_pBrianNielsen = new Bamse;
}

Håber det løser dit problem.

Mvh
Rasmus



Tornado (02-05-2001)
Kommentar
Fra : Tornado


Dato : 02-05-01 09:55

> Hvis det er tilfældet (at du bruger en pointer) behøver du ikke inkludere
> den 'modsatte' header-fil i header-filen. Istedet kan du lavet en
> forward-erklæring, og så inkludere header-filen, hvor du implementerer
> klassen:
>
> // Kylling.h
>
> // Forward decl
> class Bamse;
>
> class Kylling
> {
> ...
> Bamse *m_pBrianNielsen;
> ...
> };
>
> ---
>
> // Kylling.cpp
>
> #include "Bamse.h"
> #include "Kylling.h"
>
> Kylling::Kylling()
> {
> m_pBrianNielsen = new Bamse;
> }
>
> Håber det løser dit problem.

Det skulle gerne løse problemet - jeg har dog ikke haft mulighed for at
afprøve det endnu. Jeg må nok indrømme, at jeg ikke havde regnet med, at
løsningen ville være så 'nem' - specielt når mange, der er velkendt i C++,
opgav.

Tak skal du have.

Mvh
Theepan



Fabio Fernandes (01-05-2001)
Kommentar
Fra : Fabio Fernandes


Dato : 01-05-01 13:23

Hej Tornado

Det er en ikke så nem løsning, jeg er selv løbet inde i problemet tidligere.
Her er nogle forslage:

(1) er du *helt* sikker på, at man med en lille strukturændring kan bruge
peger til funktioner? (peger til funktioner må gerne kalde statiske
medlemsfuntkioner fra en klasse)

(2) Hvad hvis du lavede en base klasse, som har callback funktionen
defineret som virtual, og lade kylling og bamse arv fra den?

(3) Siden du ikke skal bruge mange callbacks, kunne man ikke erklære en
"extern" funktion i bamse, som kyllig kunne kalde og omvednt?

(4) Kyllyng er en attribut i Bamse, kunne man ikke lave Kylling til en
Kylling* (peger til kylling attribut), og i Bamse.H skrive i toppen
erklæringen: class Kylling; og linjen efter fortsætte definitionen af Bamse?
fx..

Bamse.H:
#include kylling
class Kylling;
class Bamse
{
Bamse* b;
};

Kylling.H:
class Bamse;
class Kylling
{
Kylling* k;
};
#include bamse.h




"Tornado" <tornado@linuxfromscratch.org> skrev i en meddelelse
news:9clu99$oen$1@news.cybercity.dk...
> Hej
>
> Jeg har et problem i C++ (til Linux).
>
> - Idéen:
> At få 2 klasser til at "snakke" sammen - dvs at kalde funktioner hos
> hinanden. Mange har spurgt mig hvad jeg skal bruge det til - så jeg vil da
> sige det, og spare et par postings - det skal bruges til event/callback.
>
> - Løsningsforslag:
> (1) At bruge pointere til funktioner. Det har jeg prøvet med, og til sidst
> opgivet det, da det skal være muligt at kalde funktioner i ukendte klasser
> (dvs. klasser kan hedde forskelligt, dog er funktion(headeren) den samme).
>
> (2) At lave en klasse pr event, også overføre referencen til klassen
istedet
> (hvilket betyder at klasserne skal nedarves fra en base/virtual klasse).
> Selv om det er lidt skræmmende at have 1 klasse pr event, da der er tale
om
> MANGE events, så kan jeg ikke se andre udveje, da forslag (1) ikke virker.




Tornado (01-05-2001)
Kommentar
Fra : Tornado


Dato : 01-05-01 15:22

Hej Fabio,

Jeg har vist ikke været særlig god til at forklare hvad jeg gerne vil (have
den skal gøre).. Jeg har prøvet at udspecificere det lidt med det svar til
'bjn' - men jeg vil da gerne prøve at omformulere mig for 3. gang ;) - Man
siger jo 3. gang er lykkens gang :)

Men først vil jeg lige svare på dine spørgsmål/foreslag :)

> (1) er du *helt* sikker på, at man med en lille strukturændring kan bruge
> peger til funktioner? (peger til funktioner må gerne kalde statiske
> medlemsfuntkioner fra en klasse)

Jeg har prøvet at lave pointere til en funktion af samme header - det
virkede fint. Da jeg så prøvede at lave den om til pointere til en funktion
i en klasse, klagede den vildt. Det jeg har fået at vide er følgende:

Pointer til en funktion:
typdef int (* minFunktion) (int a);

Pointer til en funktion i en klasse:
typedef int (Klasse::*minFunktion) (int a)

Problemet her er at "Klasse" er ukendt, indtil den klasse registrerer sig.
Dvs, jeg kan ikke skrive Klasse, da det jo er en definition på en bestemt
klasse, der hedder Klasse.

> (2) Hvad hvis du lavede en base klasse, som har callback funktionen
> defineret som virtual, og lade kylling og bamse arv fra den?

Der er tale om X antal funktioner, den ene kan kalde på den anden. Begge
klasser er vidt FORSKELLIGE, og har ingen identiske funktioner - hverkens
funktionsmæssig eller datamæssigt - relationen mellem dem er, at de
supplerer hinanden, så at sige..

> (3) Siden du ikke skal bruge mange callbacks, kunne man ikke erklære en
> "extern" funktion i bamse, som kyllig kunne kalde og omvednt?

Hvis jeg på nogen tidspunkt har skrevet at der er tale om få
callbacks/events, så må du meget undskylde. Der er nemlig tale om MANGE
events, og callbacks. - Event i den ene klasse, callback i den anden klasse

> (4) Kyllyng er en attribut i Bamse, kunne man ikke lave Kylling til en
> Kylling* (peger til kylling attribut), og i Bamse.H skrive i toppen
> erklæringen: class Kylling; og linjen efter fortsætte definitionen af
Bamse?

Det er rigtigt. I mit oprindelige program er der tale om pointere. Jeg
trimmede bare alt det "unødvendige" væk. Altså:
Bamse.h: Kylling *kylling;
Kylling.h: Bamse *bamse;

> fx..
>
> Bamse.H:
> #include kylling
> class Kylling;
> class Bamse
> {
> Bamse* b;
> };
>
> Kylling.H:
> class Bamse;
> class Kylling
> {
> Kylling* k;
> };
> #include bamse.h

Eksemplet er lidt forkert, da de skal have referencer til hinanden - dvs
"Bamse *b"; skal ligge i Kylling klassen, og omvendt - men jeg forstår fint,
hvad du vil frem til ;)

Men alt i alt - jeg vil bare gerne kunne compile de 2 klasser - hvis det
virker, så skulle det gerne virke.

Her er så situationen - en lille historie:

Kylling er den klogeste. Kyllingen har 3 poser med - blå, sort og gul.
Bamse er ikke så klog. Bamsen har 1 pose med.

Inden Bamse og Kylling går en tur i parken for at rydde op, siger kyllingen
til bamsen (binding til events):
- Hvis du finder en støvle, skal du smide den i min blå pose.
- Hvis du finder en død hund, skal du smide den i min sorte pose.
- Hvis du finder en diamant, skal du smide den i min gule pose.

Dvs, når Bamsen finder noget på jorden, finder den ud først ud af hvad det
er den, har fundet. Er det en støvle, smider den støvlen ind i den blå pose
(dvs Bamsen kalder f.eks. kylling->behandl_støvle(støvlen); ) - det samme
for de andre 2 typer, altså kylling->behandl_diamant(), og
kylling->behandl_død_hund(). Finder den noget, som den ved at Kyllingen ikke
er interesseret i, smider den det bare væk igen.

Kyllingen vil så, når den får noget i en pose, analyserer om kvaliteten er
god nok - hvordan den finder ud af om kvaliteten er ok, afhænger af hvilken
funktion, der blev kaldt. (I mellemtiden er Bamsen i gang med at finde andre
ting).
- Hvis kyllingen så kommer frem til at kvaliteten af støvlen er ok, smider
den det i Bamsens pose (dvs, Kyllingen kalder en funktion på bamsen - f.eks.
bamse->behold_ting(støvle); ).
- Men hvis kyllingen kom frem til at støvlen ikke kunne bruges, kalder den
en anden funktion på Bamsen - f.eks. bamse->smid_ting(støvle);

Og sådan går de rundt til de ikke gider mere (og samtlige omtalte poser kan
indeholde uendelig antal af noget) :)


Jeg har tænkt på at lave samtlige events i klasser (1 nedarvet klasse pr
event). Det gør det muligt at overføre referencer til hele event objekter.
F.eks.:

class Event
{
public:
virtual void onEvent(int noget);
}

class Støvle_Event: public Event
{
public:
void onEvent(int noget)
{
Gør noget;
}
}

class Diamant_Event: public Event
{
public:
void onEvent(int noget)
{
Gør noget andet;
}
}

Jeg tror at det er det, "folk" har svaret mig på - men det er IKKE
problemet. I teorien skulle den lille historie virke, og det gør det også i
andre tæt-relateret sprog som JAVA).

Problemet er - hvorfor kan jeg ikke #include "Bamse.h" fra Kylling.h - og
omvendt? Hvis jeg bryder referencen, så den kun går den ene vej, dvs bamsen
kan kalde funktioner på kyllingen, men ikke omvendt - så virker det.

Er der en løsning på det her? :)

Mvh
- me




Mogens Hansen (01-05-2001)
Kommentar
Fra : Mogens Hansen


Dato : 01-05-01 15:13

Dit problem drejer sig primært om design - jeg tror ikke at du ville ønske
at det du har skrevet skulle virke.
Hvad vil f.eks. "sizeof(Bamse)" være ? (Svar: hastigt stigende)

"Tornado" <tornado@linuxfromscratch.org> wrote in message
news:9clu99$oen$1@news.cybercity.dk...
> Jeg har et problem i C++ (til Linux).
>
> - Idéen:
> At få 2 klasser til at "snakke" sammen - dvs at kalde funktioner hos
> hinanden. Mange har spurgt mig hvad jeg skal bruge det til - så jeg vil da
> sige det, og spare et par postings - det skal bruges til event/callback.

Ofte af ideen i callback netop at den der initierer kaldet, ikke kender
typen på den der bliver kaldt. Der skal netop brydes en cirkulær
afhængighed.
Et typisk eksempel stammer fra grafiske brugerflader: en knap i dialog boks.
Dialogboksen kender typen på knappen. Knappen kender _ikke_ den konkrete
type på dialog-boksen, den ved blot at dialog-boksen er et vindue. Alligevel
kan knappen lave callback til dialogen, når der bliver trykket på den.

Nedenstående kode viser det (det er prøvet med Borland C++Builder V5 på
Windows 2000 og under Red Hat 7.0 med gcc):

class button
{
public:
button(void) : on_click(0) {}
~button()
{ delete on_click; }

void click(void)
{
if(on_click) {
(*on_click)();
}
}

template <class T>
void set_click_handler(T& t, void (T::*on_click_func)(void))
{
on_click_handler_base* new_on_click_handler = new
on_click_handler<T>(t, on_click_func);
delete on_click;
on_click = new_on_click_handler;
}

private:
class on_click_handler_base
{
public:
on_click_handler_base(void) {}
virtual ~on_click_handler_base() {}
virtual void operator()(void) = 0;
};

template <class T>
class on_click_handler : public on_click_handler_base
{
public:
typedef void (T::*on_click_func_type)(void);
on_click_handler(T& t, on_click_func_type on_click_func) :
t_(t), ocf_(on_click_func) {}

virtual void operator()(void)
{ (t_.*ocf_)(); }


private:
T& t_;
on_click_func_type ocf_;
};

on_click_handler_base* on_click;
};

class dialog
{
public:
dialog(void)
{
ok_button.set_click_handler(*this, &dialog::on_ok_clicked);
}

void click_ok_button(void)
{ ok_button.click(); }

void on_ok_clicked(void)
{
// callback function code, when ok button is clicked
}

private:
button ok_button;
};


int main(int /*argc*/, char* /*argv*/[])
{
dialog dlg;
dlg.click_ok_button();

return 0;
}

prøv at single-step igennem den.

>
> - Løsningsforslag:
> (1) At bruge pointere til funktioner. Det har jeg prøvet med, og til sidst
> opgivet det, da det skal være muligt at kalde funktioner i ukendte klasser
> (dvs. klasser kan hedde forskelligt, dog er funktion(headeren) den samme).
>

Når du overvejer "pointer til funktioner" i C++, kan det ofte betale sig at
kigge på "functor" objekter.

> (2) At lave en klasse pr event, også overføre referencen til klassen
istedet
> (hvilket betyder at klasserne skal nedarves fra en base/virtual klasse).
> Selv om det er lidt skræmmende at have 1 klasse pr event, da der er tale
om
> MANGE events, så kan jeg ikke se andre udveje, da forslag (1) ikke virker.

Du kan få compileren til at generere alle klasserne automatisk ved hjælp af
templates.

>
> - Spørgsmålet:
> Dem har jeg alt for mange af angående det her, så jeg vil istedet nøjes
med
> at stille 2 spørgsmål.
>
> 1. Hvordan får jeg løst det problem med at klasserne er i stand til at
> referere til hinanden - dvs kan include hinandens headerfiler?

Det ønsker du forhåbendtligt ikke!
Cirkulære afhængigheder er roden til _meget_ ondt.

>
> 2. Pointere til funktioner virker ikke, hvis der er tale, at de skal kunne
> kalde funktioner i klasser, hvis funtionsheaderen er den samme, men
> klasse-typen/navnet er en anden - har jeg fået at vide, og også semi
> bekræftet. Nogen der har nogle forslag til hvordan man kan? - Det vil være
> mere optimalt end at have en klasse pr event.

Det kan godt lade sig gøre.
Se kapitel 5 "Generalized Functors" i bogen "Modern C++ Design", Andrei
Alexandrescu, ISBN 0-201-70431-5.


Venlig hilsen

Mogens Hansen



Tornado (02-05-2001)
Kommentar
Fra : Tornado


Dato : 02-05-01 10:07

> Dit problem drejer sig primært om design - jeg tror ikke at du ville ønske
> at det du har skrevet skulle virke.
> Hvad vil f.eks. "sizeof(Bamse)" være ? (Svar: hastigt stigende)

Som jeg nævnte tidligere i en anden post; det skulle have være pointere til
hinanden. Pointen var at få de 2 klasser compilet hver for sig, og stadig
kende til hinanden. - Rasmus Christensen kom med en perfekt løsning på
problemet.

> Ofte af ideen i callback netop at den der initierer kaldet, ikke kender
> typen på den der bliver kaldt. Der skal netop brydes en cirkulær
> afhængighed.
> Et typisk eksempel stammer fra grafiske brugerflader: en knap i dialog
boks.
> Dialogboksen kender typen på knappen. Knappen kender _ikke_ den konkrete
> type på dialog-boksen, den ved blot at dialog-boksen er et vindue.
Alligevel
> kan knappen lave callback til dialogen, når der bliver trykket på den.
>
> Nedenstående kode viser det (det er prøvet med Borland C++Builder V5 på
> Windows 2000 og under Red Hat 7.0 med gcc):
>
> class button
> {
> public:
> button(void) : on_click(0) {}
> ~button()
> { delete on_click; }
>
> void click(void)
> {
> if(on_click) {
> (*on_click)();
> }
> }
>
> template <class T>
> void set_click_handler(T& t, void (T::*on_click_func)(void))
> {
> on_click_handler_base* new_on_click_handler = new
> on_click_handler<T>(t, on_click_func);
> delete on_click;
> on_click = new_on_click_handler;
> }
>
> private:
> class on_click_handler_base
> {
> public:
> on_click_handler_base(void) {}
> virtual ~on_click_handler_base() {}
> virtual void operator()(void) = 0;
> };
>
> template <class T>
> class on_click_handler : public on_click_handler_base
> {
> public:
> typedef void (T::*on_click_func_type)(void);
> on_click_handler(T& t, on_click_func_type on_click_func) :
> t_(t), ocf_(on_click_func) {}
>
> virtual void operator()(void)
> { (t_.*ocf_)(); }
>
>
> private:
> T& t_;
> on_click_func_type ocf_;
> };
>
> on_click_handler_base* on_click;
> };
>
> class dialog
> {
> public:
> dialog(void)
> {
> ok_button.set_click_handler(*this, &dialog::on_ok_clicked);
> }
>
> void click_ok_button(void)
> { ok_button.click(); }
>
> void on_ok_clicked(void)
> {
> // callback function code, when ok button is clicked
> }
>
> private:
> button ok_button;
> };
>
>
> int main(int /*argc*/, char* /*argv*/[])
> {
> dialog dlg;
> dlg.click_ok_button();
>
> return 0;
> }
>

Jeg har kigget på det, og det løser min anden problemstilling - nemlig det
med events; kald til funktioner i klasser, uanset klassenavn (og type, så
længe de nedarver fra samme base-klasse).

> Når du overvejer "pointer til funktioner" i C++, kan det ofte betale sig
at
> kigge på "functor" objekter.

Den er ny (functor) altså. Det jeg vil da straks søge på.

> > 1. Hvordan får jeg løst det problem med at klasserne er i stand til at
> > referere til hinanden - dvs kan include hinandens headerfiler?
>
> Det ønsker du forhåbendtligt ikke!
> Cirkulære afhængigheder er roden til _meget_ ondt.

Nej, men gør jeg nok ikke - men klasserne skulle gerne kende hinanden. ;) (-
hvilket er løst.)

> Det kan godt lade sig gøre.
> Se kapitel 5 "Generalized Functors" i bogen "Modern C++ Design", Andrei
> Alexandrescu, ISBN 0-201-70431-5.

Tak for din _geniale_ eksempel-kode. Den virker meget logisk, og kan
endvidere compiles (hvilket man ikke ser med mange eksempel-koder).

Mvh
Theepan



Mogens Hansen (03-05-2001)
Kommentar
Fra : Mogens Hansen


Dato : 03-05-01 05:08

>
> Jeg har kigget på det, og det løser min anden problemstilling - nemlig det
> med events; kald til funktioner i klasser, uanset klassenavn (og type, så
> længe de nedarver fra samme base-klasse).

Ok - du sagde bare i dit oprindelige indlæg, at det skulle bruges til
event/callback.

Venlig hilsen

Mogens Hansen



Carsten Svaneborg (01-05-2001)
Kommentar
Fra : Carsten Svaneborg


Dato : 01-05-01 13:06

Tornado wrote:
> Jeg har et problem i C++ (til Linux).
> At få 2 klasser til at "snakke" sammen - dvs at kalde funktioner hos
> hinanden. Mange har spurgt mig hvad jeg skal bruge det til - så jeg vil da
> sige det, og spare et par postings - det skal bruges til event/callback.

Har du kigget på libsigc++? Det library er netop designet til at
gøre du du vil, og der er også noget dokumenation/eksempler i
/usr/share/doc/libsigc++-devel/ (eller deromking).

Alternativt hardcore med bells and whisles, mon der findes en
c++ corba wrapper?

> 1. Hvordan får jeg løst det problem med at klasserne er i stand til at
> referere til hinanden - dvs kan include hinandens headerfiler?

Kan du ikke derive dem fra en fælles abstrakt klasse der definere
funktioner for event udsendelse og modtagelse? Og så bruge
templates til de enkelte event typer?

--
* Kurosawa: http://www.designlabs.dk/husetsbio *
* Email: Carsten dot Svaneborg at risoe dot dk *
* http://www.fys.risoe.dk/fys/External/casv/ *


Søg
Reklame
Statistik
Spørgsmål : 177558
Tips : 31968
Nyheder : 719565
Indlæg : 6408926
Brugere : 218888

Månedens bedste
Årets bedste
Sidste års bedste