December 11, 2001 Re: some more questions concerning D specs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pavel Minayev | Pavel Minayev a écrit :
> A question then.
>
> Since D doesn't have macroses, it would be hard to define
> a simple syntax for message maps. Pointers to members
> are not supported. If I want to write a GUI library for
> D, what other ways could I use then? WndProc and switch()
> (aka back to the WinAPI days)? nah, totally ditches the
> idea... anything else?
>
> Just to make this all clearer, a code that could be part
> of a GUI library:
>
> // ===================================================
> // using pointers to methods, if they were implemented
>
> class Button
> {
> void.() OnClick
>
> int WndProc(uint uMsg, WPARAM wParam, LPARAM lParam)
> {
> if (uMsg == WM_LBUTTONDOWN)
> OnClick()
> }
> }
>
> class MyForm
> {
> Button cmdOk;
>
> this()
> {
> cmdOk.OnClick = cmdOk_Click;
> }
>
> void cmdOk_Click()
> {
> // do something useful...
> }
> }
Yes, this is a common problem having an object that trap events or
calls, and just dispatch them
to an other object.
It can be done with a pointer function as you sugest or in theory with a
pointer to the destination object:
class CmdOkButton
{
MyForm* dest;
this(MyForm* frm): dest(frm) {
}
int WndProc(uint uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_LBUTTONDOWN)
dest.cmdOk_Click()
}
}
In practice it's boring: create a class for all message, and writing
functions that just transfer the call to an other
object.
I suggest (i know, it's easier to sugest than to implement):
class Button
{
void.OnClick() //note OnClick is a normal member function
int WndProc(uint uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_LBUTTONDOWN)
OnClick()
}
}
class MyForm
{
Button cmdOk;
this()
{
cmdOk.OnClick = cmdOk_Click; //yes !, see below
}
void cmdOk_Click()
{
// do something useful...
}
}
Button::OnClick is a normal (virtual) function.
cmdOk.OnClick = cmdOk_Click writes in cmdOk Virtual Table !!??
Yes. It's ok if cmdOk have its _own_ virtual table.
Overwriting a member function of a class, should instruct the compiler
that this class is what can be
called a "Transfer Class".
Transfer Classes properties:
- All objects of this type have ther _own_ Virtual Table duplicated from
the Type's one,
- they also have a table for 'this' pointer translation so that for
example cmdOk_Click function
recieve a 'this' pointer pointing on the MyForm object, NOT on the
MyForm object :: cmdOk.
Advantages:
- no more pointers to member,
- its fast
Inconvenients:
- memory consumption (cheap now),
- is it in D spirit ?
If this not too complicate to implement, i think that is the easiest way
to do the job, and after all, at low level, virtual functions
are functions pointers isn't it ?
Roland
|
December 12, 2001 Re: some more questions concerning D specs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Roland | "Roland" <rv@ronetech.com> wrote in message news:3C16714A.FB186A1F@ronetech.com... > Button::OnClick is a normal (virtual) function. > cmdOk.OnClick = cmdOk_Click writes in cmdOk Virtual Table !!?? > Yes. It's ok if cmdOk have its _own_ virtual table. > > Overwriting a member function of a class, should instruct the compiler > that this class is what can be > called a "Transfer Class". The problem here is that it's not very easy to find out if some _object_ of that class gets its vtable changed: Control cmdOk; // note: not a Button! cmdOk = new Button; cmdOk.OnClick = cmdOk_Click; > Transfer Classes properties: > > - All objects of this type have ther _own_ Virtual Table duplicated from > the Type's one, > - they also have a table for 'this' pointer translation so that for > example cmdOk_Click function > recieve a 'this' pointer pointing on the MyForm object, NOT on the > MyForm object :: cmdOk. I don't see the actual difference between this and pointers to methods. Both here and there, you store pointer to object together with pointer to function, only in your case they're separated into two tables. Making vtables dynamic (Delphi term, vtables are per object rather than per class) uses much more memory especially with complex class hierarchy while not providing any advantages (it's not faster, and - IMHO - not easier to implement). > to do the job, and after all, at low level, virtual functions are functions pointers isn't it ? Yes but we were talking about pointers to methods, right? And these are different... |
December 22, 2001 Re: some more questions concerning D specs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pavel Minayev | Pavel Minayev a écrit : > The problem here is that it's not very easy to find out if some _object_ of that class gets its vtable changed: > > Control cmdOk; // note: not a Button! > cmdOk = new Button; > cmdOk.OnClick = cmdOk_Click; > object must switch from static to dynamic virtual table at run time > I don't see the actual difference between this and pointers to methods. Both here and there, you store pointer to object together with pointer to function, only in your case they're separated into two tables. Making vtables dynamic (Delphi term, vtables are per object rather than per class) uses much more memory especially with complex class hierarchy while not providing any advantages (it's not faster, and - IMHO - not easier to implement). Franckly, if i understand well the concept, i had to remind pointer to methodes syntax in C++, as i never used them. First impression: not a very nice syntax, may be usefull, i should use them. But they had already been rejected for D. I would try to suggest a replacement, something clean, before the boss come. class Button { void.OnClick() { <default action> } int WndProc(uint uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_LBUTTONDOWN) OnClick() } } class MyForm { Button cmdOk; this() { cmdOk.OnClick = cmdOk_Click; //<-------- here } void cmdOk_Click() { // do something useful... } } MyForm myform; The goal is: - a click event on myform.cmdOk calls myform.cmdOk_Click, - of course, in cmdOk_Click, 'this' points on myform, NOT on myform.cmdOk I sugest for that, just write: cmdOk.OnClick = cmdOk_Click I think this syntax is simple and that can replace pointer to member functions. But: - it must not be too complicate to implement, - it must not slow down normal virtual function call. //------------------------------------------------------ Inthe way for implementation: A little deeper inside the machine: Lets translate from D to C to show how virtuals are: //- - - - - - - - - //Button struct Button_VT { //virtual table of Button type, created by compliler in C++/D void *OnClick(); }; struct Button { Button_VT* vt; //hidden pointer to virtual table, created and initialized by compliler in C++/D }; void virtual_Button_OnClick(Button* this) { //virtual function call mechanism (inlined) this->vt->OnClick(this); } void.Button_OnClick(Button* this) { <default action> } //- - - - - - - - - //MyForm struct MyForm_VT { //virtual table of MyForm type, created by compliler in C++/D void *cmdOk_Click(); }; struct MyForm { MyForm_VT* vt; //hidden pointer to virtual table, created and initialized by compliler in C++/D Button cmdOk; } void virtual_MyForm_cmdOk_Click(MyForm* this) { //virtual function call mechanism (inlined) this->vt->cmdOk_Click(this); } void MyForm_cmdOk_Click(MyForm* this) { do something useful... } //- - - - - - - - - //declare objects: static Button_VT button_VT; static MyForm_VT myform_VT; MyForm myform; //click event on myform.cmdOk: same as a call to: virtual_Button_OnClick(&myform.cmdOk); //------------------------------- Now lets translate from D to C "myform.cmdOk.OnClick = myform.cmdOk_Click" statement: 1° in D: "myform.cmdOk.OnClick = myform.cmdOk_Click" statement: => myform.cmdOk.vt->OnClick = MyForm_cmdOk_Click not good: - all object of class MyForm will be affected - in MyForm_cmdOk_Click, 'this' will point on myform.cmdOk instead of myform. 2° in D: "myform.cmdOk.OnClick = myform.cmdOk_Click" statement: => allocate a new Button_VT: new_Button_VT => copy button_VT to new_Button_VT => set new_Button_VT.OnClick = MyForm_cmdOk_Click => set myform.cmdOk.vt = new_Button_VT solve the first problem: myform.cmdOk switches from a static VT to a dynamic VT. the other one is a little more complicate. 3° lets create a new struct: struct Button_VT2: Button_VT { void* onclickthis; Button_VT ivt; }; and a new function: void.Button_VT2_OnClick(Button* buttonp) { ((Button_VT2*)buttonp->vt)->ivt->OnClick(((Button_VT2*)buttonp->vt)->onclickthis); } now: in D: "myform.cmdOk.OnClick = myform.cmdOk_Click" statement: => allocate a new Button_VT2: new_Button_VT2 => copy button_VT to new_Button_VT2.ivt => set new_Button_VT2.ivt.OnClick = MyForm_cmdOk_Click => set new_Button_VT2.OnClick = Button_VT2_OnClick => set new_Button_VT2.onclickthis = &myform => set myform.cmdOk.vt = new_Button_VT2 okay, it work like this: event: virtual_Button_OnClick(&myform.cmdOk) => myform.cmdOk.vt->OnClick(&myform.cmdOk) => Button_VT2_OnClick(&myform.cmdOk) => ((Button_VT2*)myform.cmdOk.vt)->ivt->OnClick(((Button_VT2*)myform.cmdOk.vt)->destobject) => MyForm_cmdOk_Click(&myform) done a little slow but acceptable Everything seems too nice. There must be something wrong there. but what ? Roland |
December 23, 2001 Re: some more questions concerning D specs | ||||
---|---|---|---|---|
| ||||
Posted in reply to NancyEtRoland | "NancyEtRoland" <nancyetroland@free.fr> wrote in message news:3C251BB2.F4053844@free.fr... > I sugest for that, just write: > > cmdOk.OnClick = cmdOk_Click > > I think this syntax is simple and that can replace pointer to member functions. Exactly what I proposed. > Everything seems too nice. There must be something wrong there. but what ? IMHO there's no need to mess with virtuals at all. In other words, once you bind the function to the event slot, you bind the _concrete function_ by its pointer-to-code, bypassing vtable. As the result, you don't have to mess with all the vtable stuff - just store the pointer to object and pointer to function itself, 8 bytes total, and call them simply. In C, that'd look like: struct event { void* object; void (*method)(void*, ...); } event; event.method(event.object, arg1, arg2, arg3); |
December 26, 2001 Re: some more questions concerning D specs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pavel Minayev | Pavel makes a critical point here that must be noted. Callbacks, very often should not be targeted at the objects that created them. That is, you note that the Button object is what "receives" the OnClick message...but it is a function in MyForm that must process it. If we go the traditional C++ route (but don't use macros) then we are left with this terrible code: //declared by the API class Button; // user code class MyForm { void cmdOk_Click(); class MyForm_Button : public Button { private: MyForm *form; public: MyForm_Button(MyForm *form) { this->form = form; }; public: void OnClick() { return form->cmdOk_Click(); }; }; MyForm_Button okButton; ... }; Then the constructor for MyForm must pass a pointer to itself to the constructor for okButton, and so on. It gets worse. The point here is that, in old C++, you can't easily declare an object that delegates its callbacks to the encapsulating object. It would be a Good Thing if D allowed this. A Side Note: It's possible to do more flexible callbacks with C++ using templates; you force the compiler to, under the covers, create a wrapper function that calls the right member function of the right class. But it's a @#$(*% pain, and won't work in D since we don't have templates there. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ] |
December 26, 2001 Re: some more questions concerning D specs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pavel Minayev |
Pavel Minayev a écrit :
> IMHO there's no need to mess with virtuals at all. In other
> words, once you bind the function to the event slot, you
> bind the _concrete function_ by its pointer-to-code, bypassing
> vtable. As the result, you don't have to mess with all the
> vtable stuff - just store the pointer to object and pointer
> to function itself, 8 bytes total, and call them simply. In
> C, that'd look like:
>
> struct event
> {
> void* object;
> void (*method)(void*, ...);
> } event;
>
> event.method(event.object, arg1, arg2, arg3);
Putting those pointers to vtable is in fact exactely the same (even if it
*looks* more messy)
but this way there is no need for "pointers to member function" type.
Roland
|
December 26, 2001 Re: some more questions concerning D specs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russ Lewis | Russ Lewis a écrit : > Pavel makes a critical point here that must be noted. Callbacks, very often should not be targeted at the objects that created them. That is, you note that the Button object is what "receives" the OnClick message...but it is a function in MyForm that must process it. If we go the traditional C++ route (but don't use macros) then we are left with this terrible code: > > //declared by the API > class Button; > > // user code > class MyForm > { > void cmdOk_Click(); > > class MyForm_Button : public Button > { > private: > MyForm *form; > public: > MyForm_Button(MyForm *form) { this->form = form; }; > public: > void OnClick() { return form->cmdOk_Click(); }; > }; > > MyForm_Button okButton; > ... > }; > That is some kind of code we all do and i would apreciate a compiler do for me: it's boring > It's possible to do more flexible callbacks with C++ using templates; you force the compiler to, under the covers, create a wrapper function that calls the right member function of the right class. But it's a @#$(*% pain, and won't work in D since we don't have templates there. > I tried too: not nicer. Next time i try to do it with pointers to methode.. in C++. Roland |
December 26, 2001 Re: some more questions concerning D specs | ||||
---|---|---|---|---|
| ||||
Posted in reply to NancyEtRoland | NancyEtRoland wrote: > I tried too: not nicer. > > Next time i try to do it with pointers to methode.. in C++. I have implemented the following in C++: struct Handler { ... }; struct ArgType { ... }; Handler MakeHandler(ArgType (*func)()); Handler MakeHandler(ArgType (*func)(ArgType)); template <class X> Handler MakeHandler(ArgType (X::*func)()); template <class X> Handler MakeHandler(ArgType (X::*func)(ArgType)); Handler overloads operator(), which takes a single ArgType value and returns an ArgType. The point here is that, using templates, I can create an arbitrary structure (kind of like a generic pointer-to-function or pointer-to-member-function) that you can then use like a function pointer. Unfortunately, my company hasn't (yet) released the source code to open source, though I'm hoping they will. Fortunately, I developed this based on stuff I found freely on the web. -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ] |
December 26, 2001 Re: some more questions concerning D specs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Russ Lewis | "Russ Lewis" <spamhole-2001-07-16@deming-os.org> wrote in message news:3C2A5F43.C58C4EEB@deming-os.org... > I have implemented the following in C++: > > struct Handler { ... }; > struct ArgType { ... }; > > Handler MakeHandler(ArgType (*func)()); > Handler MakeHandler(ArgType (*func)(ArgType)); > > template <class X> > Handler MakeHandler(ArgType (X::*func)()); > template <class X> > Handler MakeHandler(ArgType (X::*func)(ArgType)); > > Handler overloads operator(), which takes a single ArgType value and returns an > ArgType. The point here is that, using templates, I can create an arbitrary > structure (kind of like a generic pointer-to-function or pointer-to-member-function) that you can then use like a function pointer. I've came to the same solution; however, I couldn't make Visual C++ to work with that sort of templates. Borland C++, on other hand, did the job just fine. So I've made it more general (and less typesafe): template <class X> Handler MakeHandler(X func); IMO, all these efforts are a fine example of why having such a construct built-in into language would be really great! |
December 27, 2001 Re: some more questions concerning D specs | ||||
---|---|---|---|---|
| ||||
Posted in reply to Pavel Minayev | My code has to be portable between gcc on Linux and MSVC on Windows. The simple design, which works on gcc, failed on MSVC because the compiler was too simpleminded to handle it. But it can be done...I hope someday to post the MSVC solution. :( As you say, it would be a great thing to have in the language. I even posted a C-compatible solution for member function pointers in a previous post called "Member Function Pointers" (8/20/01) -- The Villagers are Online! http://villagersonline.com .[ (the fox.(quick,brown)) jumped.over(the dog.lazy) ] .[ (a version.of(English).(precise.more)) is(possible) ] ?[ you want.to(help(develop(it))) ] |
Copyright © 1999-2021 by the D Language Foundation