Home » Developing U++ » U++ Developers corner » Core chat... (reference counted pointers etc.)
Re: Core chat... [message #12350 is a reply to message #12348] |
Thu, 25 October 2007 23:38 |
|
mirek
Messages: 13975 Registered: November 2005
|
Ultimate Member |
|
|
mdelfede wrote on Thu, 25 October 2007 17:26 |
luzr wrote on Thu, 25 October 2007 21:33 |
You can try. However, long time ago, such class template was part of U++. But there was no use for it. "pick" is confusing at first, but quite powerful concept.
|
I do find "pick" quite clear. You get the top performance at at the expense of some loss of easy to use stuff.
|
Well, performance is nice, but really not that important.
What IS imporant is that there is only ONE PLACE where instance of (possibly) non-copyable object can exist.
Quote: |
array<int> a, b;
a.At(1000) = 1;
b = a; <== b and a share same memory area
a[10] = 2; <== here an automatic deep copy, ok
b[10] = 2; <== here no deep copy, just array access, ok
|
Note that above is impossible to implement reliably in C++ (as long as you want read operator[] access to perform no copy at all).
Quote: |
array a gets 2 references to it for a while, just when MyFunc returns a value. Then, temporary from MyFunc get destroyed, leaving a with a single reference. Any subsequent access to a[] don't need a deep copy.
|
Sure. Anyway, the real point of pick is here:
Array<Ctrl> CreateWidgets()
{
Array<Ctrl> x;
...
return x;
}
Mirek
|
|
|
Re: Core chat... [message #12351 is a reply to message #12350] |
Thu, 25 October 2007 23:47 |
mdelfede
Messages: 1307 Registered: September 2007
|
Ultimate Contributor |
|
|
luzr wrote on Thu, 25 October 2007 23:38 |
Well, performance is nice, but really not that important.
What IS imporant is that there is only ONE PLACE where instance of (possibly) non-copyable object can exist.
|
I don't get the point...
Quote: |
Quote: |
array<int> a, b;
a.At(1000) = 1;
b = a; <== b and a share same memory area
a[10] = 2; <== here an automatic deep copy, ok
b[10] = 2; <== here no deep copy, just array access, ok
|
Note that above is impossible to implement reliably in C++ (as long as you want read operator[] access to perform no copy at all).
|
Yes, you got the true caveat of my way.... now maybe you understand *why* I do miss __property construct in c++....
Quote: |
Sure. Anyway, the real point of pick is here:
Array<Ctrl> CreateWidgets()
{
Array<Ctrl> x;
...
return x;
}
|
uh ? My Array class behaves exactly as yours, here...
Array<Ctrl> CreateWidgets()
{
Array<Ctrl> x; <== here, a single reference to memory object
...
return x; <== here, for a while, 2 references to THE SAME memory object
}
ctrls = CreateWidgets() <== here, the first reference is destroyed, leaving a single reference in ctrls
In your pick_ behaviour, you have a single reference ever to a single memory object. In my case, I have just for a while 2 references to a single memory object, then the first one is released leaving the same result as yours.
Max
|
|
|
Re: Core chat... [message #12355 is a reply to message #12351] |
Fri, 26 October 2007 09:36 |
|
mirek
Messages: 13975 Registered: November 2005
|
Ultimate Member |
|
|
mdelfede wrote on Thu, 25 October 2007 17:47 |
Quote: |
Sure. Anyway, the real point of pick is here:
Array<Ctrl> CreateWidgets()
{
Array<Ctrl> x;
...
return x;
}
|
uh ? My Array class behaves exactly as yours, here...
Array<Ctrl> CreateWidgets()
{
Array<Ctrl> x; <== here, a single reference to memory object
...
return x; <== here, for a while, 2 references to THE SAME memory object
}
ctrls = CreateWidgets() <== here, the first reference is destroyed, leaving a single reference in ctrls
In your pick_ behaviour, you have a single reference ever to a single memory object. In my case, I have just for a while 2 references to a single memory object, then the first one is released leaving the same result as yours.
Max
|
Sure. But then, what now:
ctrls[10].Create<Button>()
(To make me more clear, "COPY" of the "COPY ON WRITE" is impossible...)
Mirek
|
|
|
Re: Core chat... [message #12360 is a reply to message #12355] |
Fri, 26 October 2007 12:13 |
mdelfede
Messages: 1307 Registered: September 2007
|
Ultimate Contributor |
|
|
luzr wrote on Fri, 26 October 2007 09:36 |
Sure. But then, what now:
ctrls[10].Create<Button>()
(To make me more clear, "COPY" of the "COPY ON WRITE" is impossible...)
|
uhmmmm... no, you were not clear enough !
Looking on Array::Create<>() is defined as
template<class TT> TT& Create() { TT *q = new TT; Add(q); return *q; }
and no Create<>() is defined for Ctrl class. So I suppose that your ctrls is an array of array of controls... maybe. Your examples becomes difficult to understand...
So, you're taking the 11-th element of your array, wich is an array of controls, and add to it a new button, ok.
Let's start from the beginnin (I'm thinking as I write, sorry!):
with ctrls[10] you get a reference to 11-th array (element must exist, but I guess it's not important). You add then a new button to it, ok. That should be equivalent to :
Array<Array<Ctrl>> a;
Array<Ctrl> b;
b.Create<Button>();
a.At(10) = b; // just to be sure that 11-th element exists...
which in your pick behaviours leaves a owning b contents and b in picked state.
I still don't see caveats in my Copy-On-Write behaviour, besides of the impossibility of using [] operator because missing lvalue-rvalue signature difference.
If I do the same with my class, I have :
Array<Array<Ctrl>> a; // empty array of arrays of Ctrl
Array<Ctrl> b; // empty array of Ctrl
b.Create<Button>(); // no problem, a button is added to b
a.At(10) = b; // a[10] adds a reference to b object... no problem
then, I do have 2 possibilities :
b[0] = myCtrl; // don't look for a while at [] problem...
b becomes a DIFFERENT array as a[10], and a[10] points to an object that has a single reference
Or... b exits from its scope, leaving object at a[10] again with a single reference.
You can join all that on a single line, just replacing this damn' [] operator with some member function OR don't care about havng copy-on-write even on reads if array has more references. Let's look at the second case (keeping []) :
Array<Array<Ctrl>> ctrls;
ctrls[10].Create<Button>()
the 11-th element of ctrls gets a button added, I don't see caveats. Where am I wrong ????
BTW, the *real* caveat of copy-on-write behaviour is the missing difference of signature between lvalues anr rvalues, which gives you 2 choices :
1- Make(if possible) operator[] returning a read-only element, and use some other function to set element value; that is the most efficient, which leads to true copy-on-write when array as more than 1 reference on it.
2- Let [] do the jobs, so you can have also copy-on-read if array has more references, which is a waste of time.
As I said before, C++ standard as *many* caveats and missing stuffs...
[Updated on: Fri, 26 October 2007 12:27] Report message to a moderator
|
|
|
Re: Core chat... [message #12361 is a reply to message #12360] |
Fri, 26 October 2007 12:42 |
|
mirek
Messages: 13975 Registered: November 2005
|
Ultimate Member |
|
|
mdelfede wrote on Fri, 26 October 2007 06:13 |
luzr wrote on Fri, 26 October 2007 09:36 |
Sure. But then, what now:
ctrls[10].Create<Button>()
(To make me more clear, "COPY" of the "COPY ON WRITE" is impossible...)
|
uhmmmm... no, you were not clear enough !
|
Sorry. My mistake. Forget about [10]. Only
ctrls.Create<Button>();
or (to be more clear):
Array<Ctrl> a, b;
a.Create<Label>();
a.Create<EditField>();
b = a;
b.Create<Label>(); // Now what?!
Mirek
[Updated on: Fri, 26 October 2007 12:45] Report message to a moderator
|
|
|
Re: Core chat... [message #12363 is a reply to message #12361] |
Fri, 26 October 2007 13:03 |
mdelfede
Messages: 1307 Registered: September 2007
|
Ultimate Contributor |
|
|
luzr wrote on Fri, 26 October 2007 12:42 |
Sorry. My mistake. Forget about [10]. Only
ctrls.Create<Button>();
or (to be more clear):
Array<Ctrl> a, b;
a.Create<Label>();
a.Create<EditField>();
b = a;
b.Create<Label>(); // Now what?!
|
Array<Ctrl> a, b; // two empty arrays, reference to nothing
a.Create<Label>(); // adds a Label to a, a becomes an array with a single reference at underlying data
a.Create<EditField>(); // adds an EditField to a, a is grown by 1 element, as it had a single reference to data, nothing strange happens.
b = a; // now a AND b have both reference to the same underlying array
b.Create<Label>(); // underlying data is copied ....
I see your point, here you *did* want a single array of controls, and you *did* want to destroy a when copying to b. You could have easily write :
Array<Ctrl> a;
a.Create<Label>();
a.Create<EditField>();
Array<Ctrl>&b = a;
b.Create<Label>();
That leaves both a and b pointing to SAME array. You then could say that when a is destroyed, b points to nothing, that's true, but I can hardly imagine such a case. For example :
Array<Ctrl> MyFunc()
{
Array<Ctrl> a;
a.Create<Label>();
a.Create<EditField>();
Array<Ctrl>&b = a;
b.Create<Label>();
return b;
}
Array<Ctrl> c = MyFunc();
still works... just before a is destroyed (and thus b points to nothing...), c adds a reference to its contents, then it remains the sole owner of it when function ends.
Let me say that in my case it's clear what you pretends to do, in yours you must know pick behaviour, and still you have the possibility to write an erroneus a.Create<SomeControl>() AFTER a is picked. In my case, you can AND you get what you want.
You may still say that if you do a.Create<SomeControl>() you get an error, ok... and I can tell you that if you forget the & you have double controls on screen!
Ciao
Max
|
|
|
Re: Core chat... [message #12367 is a reply to message #12363] |
Fri, 26 October 2007 14:01 |
|
mirek
Messages: 13975 Registered: November 2005
|
Ultimate Member |
|
|
mdelfede wrote on Fri, 26 October 2007 07:03 |
luzr wrote on Fri, 26 October 2007 12:42 |
Sorry. My mistake. Forget about [10]. Only
ctrls.Create<Button>();
or (to be more clear):
Array<Ctrl> a, b;
a.Create<Label>();
a.Create<EditField>();
b = a;
b.Create<Label>(); // Now what?!
|
Array<Ctrl> a, b; // two empty arrays, reference to nothing
a.Create<Label>(); // adds a Label to a, a becomes an array with a single reference at underlying data
a.Create<EditField>(); // adds an EditField to a, a is grown by 1 element, as it had a single reference to data, nothing strange happens.
b = a; // now a AND b have both reference to the same underlying array
b.Create<Label>(); // underlying data is copied ....
|
How is it copied? There is no copy operation for widgets. And it does not even have sense.
Quote: |
[/code]
I see your point, here you *did* want a single array of controls, and you *did* want to destroy a when copying to b. You could have easily write :
Array<Ctrl> a;
a.Create<Label>();
a.Create<EditField>();
Array<Ctrl>&b = a;
b.Create<Label>();
That leaves both a and b pointing to SAME array.
|
Of course, but that is completely different thing.
In fact, yes, one of reasons to give away with reference counting for containers (or other "smart" approaches) is that you only seldem need to transfer the value of container at all.
OTOH, if you DO need such operation, then in most cases you either need exactly "pick" behaviour or you just do not care (function return value). Very seldom you need "deep copy" - in that case, you still have optional deep operations available.
And yes, sometimes you get "broken pick semantics" runtime assert. But I get 100 times more "invalid index" asserts than this one.
Quote: |
You then could say that when a is destroyed, b points to nothing, that's true, but I can hardly imagine such a case. For example :
Array<Ctrl> MyFunc()
{
Array<Ctrl> a;
a.Create<Label>();
a.Create<EditField>();
Array<Ctrl>&b = a;
b.Create<Label>();
return b;
}
Array<Ctrl> c = MyFunc();
still works...
|
But does not compile Your template needs deep public copy constructor for T, even if it is not actually used (because reference count is always 1 for COW ops).
Quote: |
and I can tell you that if you forget the & you have double controls on screen!
|
Sounds like a possible solution, but in that case you need to solve the quite complex problem of polymorphic copy - and all that only to make flawed code working somehow.
Mirek
[Updated on: Fri, 26 October 2007 14:02] Report message to a moderator
|
|
|
|
|
Re: Core chat... [message #12375 is a reply to message #12368] |
Fri, 26 October 2007 22:09 |
|
mirek
Messages: 13975 Registered: November 2005
|
Ultimate Member |
|
|
mdelfede wrote on Fri, 26 October 2007 08:18 | Well, well... I give up !
I still think that refcounted array are very useful somewhere, but in case of widget arrays, they aren't.
And I still think that is better to have code that corrects your mistakes (refcounted...) OR displays your errors (pick_).
Going back to At() behaviour, that is one thing I really don't like!
BTW, I still don't see the point of
on an empty array... What does a[2] becomes, for example ?
Empty string ? null object ? a default one ? Does At(n) initialize the n previous elements if the array was empty ?
|
Yes, it initializes non-existing elements to default value (String()). Also, there is another 2 parameter version that provides the intialization value.
Quote: |
p.s. Did you have time to give a look at OpenGL bug, or should I look at it ?
|
I am sorry, I am not in Linux yet. Right now I am working hard to make U++ look even more native in Vista (and you need to be tough to use Vista for so long . In fact, this effort will provide more native Linux look too (the problem to solve is the visual appearence of DropList and DropChoice), so that will be the natural follow-up project.
Mirek
|
|
|
Re: Core chat... [message #12376 is a reply to message #12371] |
Fri, 26 October 2007 22:12 |
|
mirek
Messages: 13975 Registered: November 2005
|
Ultimate Member |
|
|
mdelfede wrote on Fri, 26 October 2007 12:47 |
luzr wrote on Fri, 26 October 2007 14:01 |
But does not compile Your template needs deep public copy constructor for T, even if it is not actually used (because reference count is always 1 for COW ops).
Quote: |
and I can tell you that if you forget the & you have double controls on screen!
|
Sounds like a possible solution, but in that case you need to solve the quite complex problem of polymorphic copy - and all that only to make flawed code working somehow.
|
Well, thinking a bit more about it.... I have the solution : put dummy copy constructors in Ctrl class, that throw an exception or fail an assert... So :
1- You're sure that you don't use the copy-on-write for classes you don't want to.
2- You must not write some cumbersome polymorphic class copy constructor.
Copy constructors *are* there, so my previous example does work
So, the solution (with refcounted classes) is to displace the error from picked array to copy constructor
|
Now I am not quite sure if this is meant as joke or not
(Note that classes like Ctrl derive from NoCopy, which makes it copy constructor private to catch its misuse at compile time).
Mirek
|
|
|
Re: Core chat... [message #12377 is a reply to message #12376] |
Fri, 26 October 2007 23:09 |
mdelfede
Messages: 1307 Registered: September 2007
|
Ultimate Contributor |
|
|
luzr wrote on Fri, 26 October 2007 22:12 |
Now I am not quite sure if this is meant as joke or not
(Note that classes like Ctrl derive from NoCopy, which makes it copy constructor private to catch its misuse at compile time).
|
Well, it was a joke but a working one
Seriously speaking, those are 2 opposite ways of doing the job... pick_ and refcount way, each with its own goods and bads.
It would be obviously impossible to implement your Array as a refcounted one without rewriting 50% of upp code, I guess.
Comparing things, you're catching as runtime errors the assignements to picked arrays and as compile errors the copy of Ctrl objects. With refcounted, I'd catch as runtime errors the copy of Ctrls objects and I'd have no picked problems... at a small expense of code speed. Personally I'd prefere the refcounted way *if* c++ had a way to distinguish between lvalue and rvalue on [] operator. Being (for now) the opposite, your way may be better.
Ciao
Max
|
|
|
|
|
|
|
Re: Core chat... [message #12389 is a reply to message #12387] |
Sat, 27 October 2007 11:16 |
|
mirek
Messages: 13975 Registered: November 2005
|
Ultimate Member |
|
|
mdelfede wrote on Sat, 27 October 2007 05:08 |
luzr wrote on Fri, 26 October 2007 23:55 |
I would like to too, but if you are going to develop GUI toolkit, you have to be tough... Smile
|
ehehehehe... I'd don't like to be in your place, now
Quote: |
Well, the expense is a little bit bigger than "small" too...
Reference counting means atomic increments and decrements with unstable conditions. These are very expensive to be called for each mutating operation...
|
mhhhhhhh.... I'm not so sure about it. When I have a bit more time, I'll try to rewrite Array class using refcounts, so we can make some test about it. I find the matter very interesting.
Ciao
Max
|
So do I, but I have already measured it
And, BTW, better benchmark against Vector...
(BTW2, Array does not have that At or Add reference problem - it never invalidates references).
Mirek
|
|
|
Re: Core chat... [message #12391 is a reply to message #12389] |
Sat, 27 October 2007 13:06 |
mdelfede
Messages: 1307 Registered: September 2007
|
Ultimate Contributor |
|
|
luzr wrote on Sat, 27 October 2007 11:16 |
So do I, but I have already measured it
And, BTW, better benchmark against Vector...
(BTW2, Array does not have that At or Add reference problem - it never invalidates references).
|
So I suppose your Array class is built on top of a sort of linked list... not a contiguous area, right ?
Ciao
Max
p.s.: just a small question about object ownership...
When you write
Who has the ownership of aControl ? Array a[] or who created aControl ?
In former case this :
OpenGLExample aControl;
a.Add(aControl);
Should be wrong; in the latter case this :
a.Create<OpenGLExample>();
should be wrong. As you did show the latter example on this thread, I suppose a[] has the ownership...
Ciao
Max
[Updated on: Sat, 27 October 2007 13:30] Report message to a moderator
|
|
|
Re: Core chat... [message #12392 is a reply to message #12391] |
Sat, 27 October 2007 14:50 |
|
mirek
Messages: 13975 Registered: November 2005
|
Ultimate Member |
|
|
mdelfede wrote on Sat, 27 October 2007 07:06 |
luzr wrote on Sat, 27 October 2007 11:16 |
So do I, but I have already measured it
And, BTW, better benchmark against Vector...
(BTW2, Array does not have that At or Add reference problem - it never invalidates references).
|
So I suppose your Array class is built on top of a sort of linked list... not a contiguous area, right ?
|
Basically *implemented* as Vector<T*>...
Quote: |
p.s.: just a small question about object ownership...
When you write
Who has the ownership of aControl ? Array a[] or who created aControl ?
|
Very well, getting to the real issues
If aControl is an instance of widget, such statement is simply impossible (because it requires some form of copy, which Ctrl lacks).
Quote: |
In former case this :
OpenGLExample aControl;
a.Add(aControl);
Should be wrong; in the latter case this :
|
Yes. You cannot copy widgets.
Quote: |
a.Create<OpenGLExample>();
should be wrong. As you did show the latter example on this thread, I suppose a[] has the ownership...
|
Yes, although the whole idea of "ownership" is a little bit moot here. The widgets is simply an element of the container, the "ownership" issue is simple and obvious...
Mirek
|
|
|
Re: Core chat... [message #12395 is a reply to message #12392] |
Sat, 27 October 2007 15:21 |
mdelfede
Messages: 1307 Registered: September 2007
|
Ultimate Contributor |
|
|
I did ask the former question because I was lookin' inside MainWindow code.... Looking for the OpenGL bug.
But then I realized that Ctrl::Add() is quite different from Array::Add(), building an array of references instead of objects.
BTW, I still didn't find the bug there... The only thing I found (up to now) is shown in my code here :
int zzz;
MyAppWindow *win, *win2;
win = new MyAppWindow;
win2 = new MyAppWindow;
OpenGLExample gl, gl2;
gl.SetFrame(InsetFrame());
gl2.SetFrame(InsetFrame());
win->Add(gl.HSizePos(10, 10).VSizePos(10, 10));
win2->Add(gl2.HSizePos(10, 10).VSizePos(10, 10));
win->Sizeable().Zoomable();
win2->Sizeable().Zoomable();
zzz = Ctrl::GetTopCtrls().GetCount(); // zzz = 0
win->OpenMain();
zzz = Ctrl::GetTopCtrls().GetCount(); // zzz = 1
win2->OpenMain();
zzz = Ctrl::GetTopCtrls().GetCount(); // zzz = 2
delete win;
zzz = Ctrl::GetTopCtrls().GetCount(); // zzz = 2 !!!
delete win2;
zzz = Ctrl::GetTopCtrls().GetCount(); // zzz = 2 !!!
Ctrl::EventLoop();
If I suppress the lines :
win->Add(gl.HSizePos(10, 10).VSizePos(10, 10));
win2->Add(gl2.HSizePos(10, 10).VSizePos(10, 10));
The code works ok :
zzz = Ctrl::GetTopCtrls().GetCount(); // zzz = 0
win->OpenMain();
zzz = Ctrl::GetTopCtrls().GetCount(); // zzz = 1
win2->OpenMain();
zzz = Ctrl::GetTopCtrls().GetCount(); // zzz = 2
delete win;
zzz = Ctrl::GetTopCtrls().GetCount(); // zzz = 1
delete win2;
zzz = Ctrl::GetTopCtrls().GetCount(); // zzz = 0
That works even if I leave both lines BUT OpenGLExample is *not* derived from GLControl.
I'd like to know if the bug is Linux-dependent or not... But I haven't an Ide setup on my win xp machine. Don't you have a bit time to test on windows ?
Back to refcounted objects. What about if Ctrl would be an object built with PIMPL idiom and refcounted ? You then could write :
aControl a; // control is created
aControl b = a; // just reference to inner pimpl object is copied
or, what sound even better:
Vector<Ctrl>*a, *b;
Ctrl c; // control is created, RefCount == 1
a = new Vector<Ctrl>;
b = new Vector<Ctrl>;
a->Add(c); // a gets a *copy* of c, but in reality it adds just to refcount of c, that becomes 2
b->Add(c); // b gets a *copy* of c, but in reality it adds just to refcount of c, that becomes 3
delete a; // a gets destroyed, RefCount in c becomes 2
The advantage of this instead of references of an object ? Well... you must not care of ownership.... and you can be sure object is freed on last reference lost.
As usual, that brings some performance lost.
Ciao
Max
|
|
|
Goto Forum:
Current Time: Mon Apr 29 17:17:29 CEST 2024
Total time taken to generate the page: 0.02855 seconds
|