|
| Funktion som argument til en funktion Fra : Thomas Arildsen |
Dato : 01-12-01 13:41 |
|
Jeg har en klasse, hvori jeg har erklæret en funktion som:
void Error(char* s)
Jeg har så en anden funktion, som er erklæret nogenlunde i den her stil:
funktion(void EnFunktion(char*))
Der skulle man jo umiddelbart tro, at jeg kunne køre:
funktion(Error)
, men det kan ikke lade sig gøre, og compileren fortæller at problemet er,
at funktion tager et 'void (char *)'-argument, men at funktionen Error
svarer til et 'void (__cdecl *) (char *)'-argument. Jeg har fra tidligere
erfaring med, at funktionerne tilsyneladende får disse mærkelige returtyper,
når de er members i en klasse, men hvad i alverden kan man gøre ved det. Der
skulle vel i princippet ikke være noget galt i på denne måde at overføre en
funktion som argument?
Mvh. Thomas Arildsen
| |
Thomas Lykkeberg (01-12-2001)
| Kommentar Fra : Thomas Lykkeberg |
Dato : 01-12-01 13:56 |
|
On Sat, 1 Dec 2001 13:41:11 +0100, "Thomas Arildsen"
<tari00@kom.auc.dk> wrote:
>Jeg har en klasse, hvori jeg har erklæret en funktion som:
>
>void Error(char* s)
Denne funktion modtager en (char *) og returnerer en (void)
>Jeg har så en anden funktion, som er erklæret nogenlunde i den her stil:
>
>funktion(void EnFunktion(char*))
???? kan det lade sig gøre??
>Der skulle man jo umiddelbart tro, at jeg kunne køre:
>
>funktion(Error)
Nej.
Det du gør er at overføre en pointer to funktionen Error. Det bliver
void (__cdecl *)(char *).
Hvis du vil kalde din Error funktion med en pointer til en funktion
som returnerer en (void) og modtager en (char *) skal du gøre
følgende:
typedef void (fPointer *)(char *);
Dette definerer en pointer type "fPointer" som er en pointer til en
funktion der returnerer en (void) og modtager en (char *).
void funktion(fPointer p)
{
// Gør noget med p
}
kunne også skrives direkte:
void funktion(void (p *)(char *))
{
// Gør noget med p
}
så kan du kalde din "funktion" på følgende måde.
funktion(Error);
Jeg kan dog ikke helt se formålet med denne operation??
>, men det kan ikke lade sig gøre, og compileren fortæller at problemet er,
>at funktion tager et 'void (char *)'-argument, men at funktionen Error
>svarer til et 'void (__cdecl *) (char *)'-argument. Jeg har fra tidligere
>erfaring med, at funktionerne tilsyneladende får disse mærkelige returtyper,
>når de er members i en klasse, men hvad i alverden kan man gøre ved det. Der
>skulle vel i princippet ikke være noget galt i på denne måde at overføre en
>funktion som argument?
Ja, se ovenfor.
| |
Thomas Arildsen (01-12-2001)
| Kommentar Fra : Thomas Arildsen |
Dato : 01-12-01 14:25 |
|
"Thomas Lykkeberg" <thomasDOTlykkeberg@privatDOTdk> wrote in message
> Jeg kan dog ikke helt se formålet med denne operation??
Ja, det hele er en længere historie, som det kan være, der findes en langt
bedre løsning til. Problemet er, at jeg har et objekt af en bestemt klasse,
som indeholder et andet objekt af en anden klasse. Den ene klasse er
erklæret i en fil, f.eks. "klasse1.h" og den anden er erklæret i en anden
fil, f.eks. "klasse2.h". Problemet er nu, at mens klasse1 indholder objekter
af klasse2, skal disse objekter af klasse2 også kunne køre funktioner ovre i
objektet af klasse1, som indeholder dem. Det blev i første omgang forsøgt
gjort ved at overføre en pointer til klasse1-objektet (this) til
klasse2-objektet under oprettelsen af dette. Dette kræver imidlertid, at
"klasse1.h" #include'rer "klasse2.h", og "klasse2.h" #include'rer
"klasse1.h", hvilket giver en eller anden mærkelig form for problem, som
gør, at compileren ikke vil læse koden. Jeg har ellers forsøgt at omgå
problemer med f.eks. multiple inkluderinger af mine .h-filer med en
#ifndef KLASSE1_H
#define KLASSE1_H
....
#endif
omkring al koden, men det hjælper ikke. Nu er jeg ikke så vant til C++, så
jeg kender ikke til alle måderne at gøre sådan noget på, og så tænkte jeg,
at jeg måske istedet bare kunne give klasse2-objektet en pointer til den
funktion i klasse1-objektet, som den skulle køre, hvilket jeg så heller ikke
umiddelbart kan få til at fungere.
I praksis er klassen, eksemplificeret ved klasse1, et Qt-brugerfladevindue,
og klasse2 er en klasse, som implementerer noget underliggende
funktionalitet i selve applikationen. Jeg har så en funktion i
brugerflade-klassen, som åbner et vindue og skriver en fejlmeddelelse heri,
og den vil jeg gerne have, at objekter af klasse2 kan køre, hvis der sker en
fejl heri, så brugeren bliver informeret om det.
Mvh. Thomas Arildsen
| |
Thomas Lykkeberg (01-12-2001)
| Kommentar Fra : Thomas Lykkeberg |
Dato : 01-12-01 14:47 |
|
On Sat, 1 Dec 2001 14:24:41 +0100, "Thomas Arildsen"
<tari00@kom.auc.dk> wrote:
>................. Dette kræver imidlertid, at
>"klasse1.h" #include'rer "klasse2.h", og "klasse2.h" #include'rer
>"klasse1.h", hvilket giver en eller anden mærkelig form for problem, som
>gør, at compileren ikke vil læse koden. Jeg har ellers forsøgt at omgå
>problemer med f.eks. multiple inkluderinger af mine .h-filer med en
Det er der nu ikke noget underligt i. Det kaldes recursiv inlcude
problemet. Du inkluderer en .h fil i en .h fil som inkluderer den fil
som lige har inkluderet den..... blaaa det fortsætter i slt
uendelighed ( eller indtil compileren bliver træt), du forstår sikkert
hvad jeg mener. Der er ikke noget grund til at du inkluderer i dine .h
filer, i sådan et tilfælde skal du kun inkluderer i dine .c filer.
>omkring al koden, men det hjælper ikke. Nu er jeg ikke så vant til C++, så
>jeg kender ikke til alle måderne at gøre sådan noget på, og så tænkte jeg,
>at jeg måske istedet bare kunne give klasse2-objektet en pointer til den
>funktion i klasse1-objektet, som den skulle køre, hvilket jeg så heller ikke
>umiddelbart kan få til at fungere.
>I praksis er klassen, eksemplificeret ved klasse1, et Qt-brugerfladevindue,
>og klasse2 er en klasse, som implementerer noget underliggende
>funktionalitet i selve applikationen. Jeg har så en funktion i
>brugerflade-klassen, som åbner et vindue og skriver en fejlmeddelelse heri,
>og den vil jeg gerne have, at objekter af klasse2 kan køre, hvis der sker en
>fejl heri, så brugeren bliver informeret om det.
Sådan som jeg læser det har du en klasse som beskriver din bruger
grænseflade (klasse1), og en klasse som beskriver en Error dialog box?
Det du gerne vil er at have mulighed for at åbne denne generelle Error
dialog hvorsom helst i din application?
Kunne løses meget enkelt, men først skal man jo lige vide vilket
udviklings miljø du bruger? Borland Builder, Delphi eller VC++?
/Thomas
| |
Thomas Arildsen (01-12-2001)
| Kommentar Fra : Thomas Arildsen |
Dato : 01-12-01 14:59 |
|
"Thomas Lykkeberg" <thomasDOTlykkeberg@privatDOTdk> wrote in message
news:rcnh0ucpa19jv3t98hp6qogeljsojrf82d@4ax.com...
> Det er der nu ikke noget underligt i. Det kaldes recursiv inlcude
> problemet. Du inkluderer en .h fil i en .h fil som inkluderer den fil
> som lige har inkluderet den..... blaaa det fortsætter i slt
> uendelighed ( eller indtil compileren bliver træt), du forstår sikkert
> hvad jeg mener. Der er ikke noget grund til at du inkluderer i dine .h
> filer, i sådan et tilfælde skal du kun inkluderer i dine .c filer.
Nå ja, godt du siger det. der er ingen af klasse2-objekterne, som skal være
faste members i klasse1 (de benyttes kun under nogle bestemte funktioner),
så det kan godt ryge over i .c-filen.
> Kunne løses meget enkelt, men først skal man jo lige vide vilket
> udviklings miljø du bruger? Borland Builder, Delphi eller VC++?
Jeg bruger VC++ 6.0. Jeg prøver lige, om jeg ikke kan få det op at stå på
den anden måde først.
Tak for påmindelsen.
Mvh. Thomas Arildsen
| |
Thomas Arildsen (01-12-2001)
| Kommentar Fra : Thomas Arildsen |
Dato : 01-12-01 15:05 |
|
"Thomas Lykkeberg" <thomasDOTlykkeberg@privatDOTdk> wrote in message
> Kunne løses meget enkelt, men først skal man jo lige vide vilket
> udviklings miljø du bruger? Borland Builder, Delphi eller VC++?
Hvis der findes en god metode, må jeg hellere blive indviet i det.
Mvh. Thomas Arildsen
| |
Thomas Lykkeberg (01-12-2001)
| Kommentar Fra : Thomas Lykkeberg |
Dato : 01-12-01 15:12 |
|
On Sat, 1 Dec 2001 15:04:34 +0100, "Thomas Arildsen"
<tari00@kom.auc.dk> wrote:
>Hvis der findes en god metode, må jeg hellere blive indviet i det.
>
Ja ,det må man sige. Hvis du blot vil have en dialogbox frem, med en
simpel meddelelse, kan du jo benytte dig af AfxMessageBox() funktionen
i VC++. Her kan du angive om du vil have [OK], [CANCEL] eller
kombinationer deraf, samt hvilket ikon, og sidst men ikke mindst, en
tekst streng du vil have til at stå i dialogboksen.
Hvis du derimod vil lave din helt egen specielle dialog box, skal du
implementere den som en klasse, men nedarv da fra en kendt CDialog
forekesempel. Når du så får brug for den opretter du bare et object af
denne type (MyDialog = new CMyDialog(FALSE);) og kalder
MyDialog->ShowModal(); og inspicerer returværdien. MyDialog skal blot
være en privat pointer, ærkleret i din klasse.
Håber det kan hjælpe dig lidt på vej, ellers må du komme efter hjælp
/Thomas
| |
Thomas Arildsen (01-12-2001)
| Kommentar Fra : Thomas Arildsen |
Dato : 01-12-01 15:24 |
|
Du skal have mange tak for hjælpen.
Jeg har nu godtnok valgt stadig at give klasse2-objekterne den føromtalte
reference tilbage til brugerfladen, hvilket fungerer nu.
Mvh. Thomas Arildsen
| |
Mogens Hansen (01-12-2001)
| Kommentar Fra : Mogens Hansen |
Dato : 01-12-01 16:07 |
|
"Thomas Arildsen" <tari00@kom.auc.dk> wrote in message
news:9ualhb$553$1@sunsite.dk...
> "Thomas Lykkeberg" <thomasDOTlykkeberg@privatDOTdk> wrote in message
>
> I praksis er klassen, eksemplificeret ved klasse1, et
Qt-brugerfladevindue,
> og klasse2 er en klasse, som implementerer noget underliggende
> funktionalitet i selve applikationen. Jeg har så en funktion i
> brugerflade-klassen, som åbner et vindue og skriver en fejlmeddelelse
heri,
> og den vil jeg gerne have, at objekter af klasse2 kan køre, hvis der sker
en
> fejl heri, så brugeren bliver informeret om det.
>
Generelt bør det undgås at have cirkulære referencer - altså at klasse1
afhænger af klasse2 og omvendt.
Som jeg umiddelbart læser det har du noget bruggrænseflade kode i klasse1 og
noget "business logic" i klasse2.
Jeg forstår det umiddelbart som at klasse2 skal holdes uafhængig af
brugergrænsefladen - hvilket er fornuftigt.
Men som jeg læser det, bliver klasse2 alligevel afhængig af klasse1, uden at
det egentlig var meningen. Det er naturligt at klasse1 afhænger af klasse2.
Den nemmeste løsning på problemet, syntes umiddelbart at være at bruge
exceptions til fejlhåndtering.
Noget i retning af (det er ikke komplet, og kan ikke umiddelbart
compileres):
void gui_class::do_something(void)
{
try {
// ...
logic.calculate(arg1, arg2);
// ...
}
catch(const std::exception& x) {
error(x.what());
}
}
Hvis du alligevel vil holde den struktur, du oprindeligt foreslog, så bliver
det noget i retningen af
// Begin GUI.H
#include <logic.h>
class gui_class
{
public:
void do_something(void);
void error(const char* message);
private:
logic_class logic;
};
// End GUI.H
// Begin LOGIC.H
// Forward declaration
class logic_class
{
public:
void calculate(gui_class& gui);
};
// End LOGIC.H
// Begin LOGIC.CPP
#include <gui.h>
void logic_class::calculate(gui_class& gui)
{
// ...
if(failed) {
gui.error("Error Message");
}
}
// End LOGIC.CPP
Det _er_ muligt at lave en fuldstændig afkobling, så logic_class ikke
afhænger af gui_class - f.eks. ved hjælp af templates.
Venlig hilsen
Mogens Hansen
| |
Thomas Arildsen (01-12-2001)
| Kommentar Fra : Thomas Arildsen |
Dato : 01-12-01 17:30 |
|
"Mogens Hansen" <mogens_h@dk-online.dk> wrote in message
news:9uarh3$lsi$2@news.cybercity.dk...
> Den nemmeste løsning på problemet, syntes umiddelbart at være at bruge
> exceptions til fejlhåndtering.
Jeg benytter allerede exception-håndtering, som fint løser mange af mine
problemer, men visse funktioner i klasse2-objekterne kører i tråde, og
exceptions kastet heri kan åbenbart fanges fra brugerfladen. Jeg kender ikke
detaljerne i, hvad dette skyldes, men jeg tror kun, at exceptions propagerer
op gennem kald-stakken tilbage til de funktioner, som direkte har kaldt
funktionerne, der kaster exceptions'ene, og funktioner der har kaldt disse
osv. Er det ikke nogenlunde rigtigt? Problemet er så, at når jeg opretter en
tråd (i Windows, med CreateThread), som kører den funktion, der kaster
exceptions'ene, har jeg ikke direkte kaldt denne funktion, og så bliver jeg
nødt til at gribe exceptions'ene ude i en funktion, som har. Den funktion
ligger så ikke i brugerfladeobjektet, og jeg bliver derfor nødt til at fuske
lidt, for at denne funktion kan "se" brugerfladen.
I virkeligheden skyldes det nok bare, at jeg ikke ved nok om
exceptionhandling og tråde, specielt i sammenhæng, så hvis du kender en
bedre måde, vil jeg naturligvis gerne høre om det.
Mvh. Thomas Arildsen
| |
Thomas Arildsen (01-12-2001)
| Kommentar Fra : Thomas Arildsen |
Dato : 01-12-01 17:33 |
|
"Thomas Arildsen" <tari00@kom.auc.dk> wrote in message
news:9ub0ck$2cu$1@sunsite.dk...
> Jeg benytter allerede exception-håndtering, som fint løser mange af mine
> problemer, men visse funktioner i klasse2-objekterne kører i tråde, og
> exceptions kastet heri kan åbenbart
Her skulle selvfølgelig stå ...åbenbart _ikke_ fanges...
> fanges fra brugerfladen.
Mvh. Thomas Arildsen
| |
Mogens Hansen (01-12-2001)
| Kommentar Fra : Mogens Hansen |
Dato : 01-12-01 18:07 |
|
"Thomas Arildsen" <tari00@kom.auc.dk> wrote in message
news:9ub0i3$2la$1@sunsite.dk...
> "Thomas Arildsen" <tari00@kom.auc.dk> wrote in message
> news:9ub0ck$2cu$1@sunsite.dk...
> > Jeg benytter allerede exception-håndtering, som fint løser mange af mine
> > problemer, men visse funktioner i klasse2-objekterne kører i tråde, og
> > exceptions kastet heri kan åbenbart
>
> Her skulle selvfølgelig stå ...åbenbart _ikke_ fanges...
>
> > fanges fra brugerfladen.
OK - det forstår jeg bedre.
Lav en try/catch i entry-funktionen til tråden, og går noget fornuftigt i
catch.
Venlig hilsen
Mogens Hansen
| |
Mogens Hansen (01-12-2001)
| Kommentar Fra : Mogens Hansen |
Dato : 01-12-01 18:05 |
|
"Thomas Arildsen" <tari00@kom.auc.dk> wrote in message
news:9ub0ck$2cu$1@sunsite.dk...
> "Mogens Hansen" <mogens_h@dk-online.dk> wrote in message
> news:9uarh3$lsi$2@news.cybercity.dk...
> > Den nemmeste løsning på problemet, syntes umiddelbart at være at bruge
> > exceptions til fejlhåndtering.
>
> Jeg benytter allerede exception-håndtering, som fint løser mange af mine
> problemer, men visse funktioner i klasse2-objekterne kører i tråde, og
> exceptions kastet heri kan åbenbart fanges fra brugerfladen. Jeg kender
ikke
Det forstår jeg ikke umiddelbart.
Er du _sikker_ på at exceptions kastes i een tråd og fanges i en anden ?
Tjek med debuggeren at det forholder sig sådan. Vær opmærksom på MS-Windows
vinduer har thread-affinitet, hvilket betyder at at den tråd, der opretter
et vindue også er den tråd, der eksekverer håndtereringen af messages til
vinduet.
Vær opmærksom på at exceptions ikke bør passere ind igennem f.eks.
MS-Windows API'et.
> detaljerne i, hvad dette skyldes, men jeg tror kun, at exceptions
propagerer
> op gennem kald-stakken tilbage til de funktioner, som direkte har kaldt
> funktionerne, der kaster exceptions'ene, og funktioner der har kaldt disse
Det lyder kompliceret hvad du beskriver.
Det gør at du nødvendigvis må forstå alle detaljerne, hvis du vil have det
til at fungere robust.
> osv. Er det ikke nogenlunde rigtigt? Problemet er så, at når jeg opretter
en
> tråd (i Windows, med CreateThread), som kører den funktion, der kaster
> exceptions'ene, har jeg ikke direkte kaldt denne funktion, og så bliver
jeg
> nødt til at gribe exceptions'ene ude i en funktion, som har. Den funktion
> ligger så ikke i brugerfladeobjektet, og jeg bliver derfor nødt til at
fuske
> lidt, for at denne funktion kan "se" brugerfladen.
Opfat exceptionen som et resultat fra funktionen, og send exceptionen
tilbage til brugergrænseflade tråden på samme måde som du vil sende ethvert
andet resultat til brugergrænsefladen.
Se eventuelt "Polymorphic Future" design pattern som må stå beskrevet i
bl.a.
Pattern-Oriented Software Architecture: Patterns for Concurrent and
Networked Objects
Douglas C. Schmidt
Michael Stal
Hans Rohnert and
Frank Buschmann
ISBN 0-471-60695-2
http://www.cs.wustl.edu/~schmidt/POSA/
Det er i det hele taget en god bog om patterns og multithreading
> I virkeligheden skyldes det nok bare, at jeg ikke ved nok om
> exceptionhandling og tråde, specielt i sammenhæng, så hvis du kender en
> bedre måde, vil jeg naturligvis gerne høre om det.
Sammenhængen mellem tråde og exceptions er ikke velbeskrevet, idet C++ i
definitionen ikke har noget begreb om tråde. Jeg mener ikke at exceptions
kan krydse tråde, uden at man gør det manuelt, og exceptions bør ikke
passere ind i et C baseret API.
Jeg håber at du er i gang med at løse et _kompliceret_ problem, ellers ville
jeg gå efter en simpel løsning (f.eks. uden flere tråde).
Venlig hilsen
Mogens Hansen
| |
Mogens Hansen (01-12-2001)
| Kommentar Fra : Mogens Hansen |
Dato : 01-12-01 16:06 |
|
"Thomas Lykkeberg" <thomasDOTlykkeberg@privatDOTdk> wrote in message
news:o9kh0uoh5e5vpl54b2igaiktut8a7nufg5@4ax.com...
> On Sat, 1 Dec 2001 13:41:11 +0100, "Thomas Arildsen"
> <tari00@kom.auc.dk> wrote:
>
> >Jeg har en klasse, hvori jeg har erklæret en funktion som:
> >
> >void Error(char* s)
> Denne funktion modtager en (char *) og returnerer en (void)
>
> >Jeg har så en anden funktion, som er erklæret nogenlunde i den her stil:
> >
> >funktion(void EnFunktion(char*))
> ???? kan det lade sig gøre??
Næsten:
funktion(void (EnFunktion*)(char*));
Blot kan Error _ikke_ bruges, fordi den er en member funktion
>
> >Der skulle man jo umiddelbart tro, at jeg kunne køre:
> >
> >funktion(Error)
> Nej.
>
> Det du gør er at overføre en pointer to funktionen Error. Det bliver
> void (__cdecl *)(char *).
__cdecl er vist noget platformsspecikt noget.
Error er en member funktion, så den er ikke af ovenstående type.
>
> Hvis du vil kalde din Error funktion med en pointer til en funktion
> som returnerer en (void) og modtager en (char *) skal du gøre
> følgende:
>
> typedef void (fPointer *)(char *);
nærmere
typedef void (ErrorHandler::*fPointer)(char*);
hvis klassen som Error er member i hedder ErrorHandler.
Venlig hilsen
Mogens Hansen
| |
Mogens Hansen (02-12-2001)
| Kommentar Fra : Mogens Hansen |
Dato : 02-12-01 08:47 |
|
"Mogens Hansen" <mogens_h@dk-online.dk> wrote in message
news:9uarh0$lsi$1@news.cybercity.dk...
> > >
> > >funktion(void EnFunktion(char*))
> > ???? kan det lade sig gøre??
>
> Næsten:
>
> funktion(void (EnFunktion*)(char*));
Det var forkert.
funktion( void (*EnFunktion)(char*) );
er vist bedre
Venlig hilsen
Mogens Hansen
| |
Thomas Arildsen (04-12-2001)
| Kommentar Fra : Thomas Arildsen |
Dato : 04-12-01 23:07 |
|
"Mogens Hansen" <mogens_h@dk-online.dk> wrote in message
news:9ucqdu$2u2e$1@news.cybercity.dk...
>
> "Mogens Hansen" <mogens_h@dk-online.dk> wrote in message
> news:9uarh0$lsi$1@news.cybercity.dk...
> > > >
> > > >funktion(void EnFunktion(char*))
> > > ???? kan det lade sig gøre??
> >
> > Næsten:
> >
> > funktion(void (EnFunktion*)(char*));
>
> Det var forkert.
> funktion( void (*EnFunktion)(char*) );
> er vist bedre
Tak for al hjælpen. Jeg har valgt at benytte
"genbrugbarhedsfjendsk-klasseimplementation-metoden" og lave det med en
pointer tilbage til brugerfladevinduet, men jeg må huske de mange gode råd
til næste gang, jeg får brug for noget lignende.
Mvh. Thomas Arildsen
| |
|
|