"Mogens Hansen" <mogens_h@dk-online.dk> wrote in message
news:ajjfe2$q0j$1@news.cybercity.dk...
> [8<8<8<]
> > Det burde give en closure.
> Det findes som en _overflødig_ sprogudvidelse i Borland C++Builder:
> "__closure"
Jeg vidste næsten at det ville komme... Jeg kender godt denne
Borland-specialitet som de har udviklet udelukkende for at kunne lave
OnFisOgBallade event-handlere.
Man kan spekulere over hvorfor Borland har udvidet C++-sproget med denne
feature. Du siger at den er overflødig. Men hvad vil du ellers have at typen
af
&myobject.mymethod
skal være? (Den er jo void(__closure*)(int) i Borland - og en syntaksfejl i
C++).
Eller vil du hellere skrive
method_wrapper1<void, int>(myobject, &MyClass::mymethod)
Iøvrigt løser Borlands extension overhovedet ikke det evige problem med at
objektet kunne gå hen og dø før closuren dør.
> Så kan man skrive
>
> void (__closure *derivedClosure)(int);
> derivedClosure = derivedObject.new_func; // Get a pointer to the
> 'new_func' member.
Ja, det er også meget fint...
Men typen void(__closure *)(int) har desværre *intet som helst* med typen
void(*)(int) at gøre, og er dermed stort set ubrugelig. Den første *burde*
være en subtype af den anden, for den anden siger bare "pointer til noget
som kan kaldes med en int for at returnere void". En member kan kaldes på
den måde, men den kan bare ikke repræsenteres som en funktionspointer. Hvis
jeg holder en void(*)(int) så kunne jeg som programmør i princippet være
ligeglad med hvordan denne funktion er implementeret. En closure ville være
fin for mig, hvis det ikke lige var fordi alting skal være så fa'ens "exact
binary layout and static dispatch"-agtigt. Det er *kun* derfor man ikke
kan - der ville ikke være noget type-mæssigt galt som sådan.
Det samme kan man sige om en funktion
void g(int x , int y=10);
Den burde have en type som var en subtype af void(&)(int), for jeg kan jo se
med mine egne øjne at den kan kaldes med ét int-argument. Men nænej, vi må
endeligt ikke *holde* funktionen i en variabel der siger at den kan tage et
int-argument - kun kalde den som sådan en.
Hvorfor kan
g(42);
give mening, når
<sometype> g2=g;
g2(42);
ikke giver mening for nogen helst <sometype>. Mine indvolde fortæller mig at
der er noget galt.
Man kunne også blive lidt mere ivrig:
void h(double d);
Den kan jo også godt kaldes med en int, men kun ved at nævne h direkte, ikke
ved at lagre h i en variabel der siger at den er en funktion der kan tage en
int. Så h kan tage en int, men ikke rigtig alligevel...
> [8<8<8<]
> > Det kan man heldigvis også i mere moderne
> > sprog.
>
> Bare for god ordens skyld:
> Det kan man også i C++.
Nej, man kan *implementere* det i C++ - der er en stor forskel.
Hvis jeg vil erklære en funktion, foo, der tager en callback, vil du så
virkelig have at jeg skal skrive:
class Callback_int_void_abstract
{
public:
virtual void operator()(int) = 0;
};
// Wrapper of the above interface for ordinary C++ functions, since their
// C++ type is not what we want here
class Callback_int_void_ordinaryfunction
{
void(*f)(int);
public:
Callback_int_void_ordinaryfunction(void(*ff)(int)) : f(ff) { }
virtual void operator()(int x) { f(x); }
};
// Wrapper of the above interface for ordinary C++ member functions
template <class T>
class Callback_int_void_memberfunction
{
T& t;
void(T::*f)(int);
public:
Callback_int_void_memberfunction(T& tt, void(*ff)(int))
: t(tt), f(ff) { }
}
void foo(std::auto_ptr<Callback_int_void_abstract> cb); // takes ownership
// endelig kan jeg kalde foo således:
Callback_int_void_abstract* mycallback
= new Callback_int_void_memberfunction(myobject, &MyClass::mymethod);
foo(mycallback);
Hvor længe gider man det? Alt dette kan i andre sprog kan gøres som
foo(myobject.mymethod)
Der er utvivlsomt noget Boost-halløj der allerede har implementeret dele af
det ønskede. Men min pointe er at man ikke kan gøre det i sproget, man kan
implementere et "bedre sprog" ovenpå sproget - der er en stor forskel.
Desuden vil Boost, dig, mig, og alle mulige andre, have hver sin definition
af Callback_int_void_abstract, så det hele ender i et stort miskmask af
wrappere i stedet for at lade sprogets egne typer være direkte anvendelige
(uden at skulle wrappes).
I ML ville man sige noget i retning af:
foo :: (int -> unit) -> unit
slut, prut, finale. Den kan enhver dims af typen (int -> unit), uanset
hvordan dimsen har realiseret den type.
I Python kan man ikke erklære foo (derved slipper man også for en masse af
ovenstående pjat, fristes man til at sige
![Blink](images/smileys/wink.gif)
. Men man kan _definere_ én af
slagsen så nemt som:
def foo(callback):
blabla
callback(42)
stored_callback = callback
osv...
def g(x, y=10): return x+y
foo(g) # virker
foo(myobj.mymethod) # virker også
foo("prut") # virker ikke. Jeg får en runtimefejl,
# der siger at "prut"(42) ikke giver mening.
> Man kan lave en C++ funktion (á la streamcallback), som frit kan tage hvad
> som helst, som kan kaldes med passende argumenter - uanset om det er
globale
> funktioner, member funktioner eller funktions objekter
> Det er kendt viden hvordan man gør.
Men det hjælper jo ikke mig, når jeg skal kalde denne function:
void foo(void(*callback)(int))
Jeg kan ikke bruge til noget at ham der skrev den funktion, *kunne* have
lavet den mere generisk. Hvis den ikke er det.
Jeg kan ikke forhindre andre i at skrive funktioner, som jeg skal kalde, på
denne måde. Jeg kan ikke tvinge dem til at bruge boost, eller hvad man nu
p.t. synes er fedt.
Problemet er, her som så mange andre steder, at C++'s typer er konkrete. Jeg
skal eksplicit skrive noget kode for at erklære abstrakte typer, som jeg så
må wrappe C++´s egne konkrete typer i.
Bjarke