f



Move constructor. Move semantics experiment.

Hi:
  A couple of days ago, I posted a letter "Is swap_void(void*)
possible?" which is about the move semantics of objects in a
dynamic array.
http://groups.google.com.tw/group/comp.lang.c++.moderated/browse_frm/thread/74dfca376425c281/74f636d8724862a7?q=swap_void&rnum=1#74f636d8724862a7

David Abrahams provided a useful link:
 http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html
which is a proposal about rvalue reference in C++.

Now my practice of the dynamic array is like this:

template<typename T>
class MyVect {
    T *_bgn, *_end, *_storage_end;
  public:
    ...
    void objmove(MyVect& target) throw()
       {
         target._bgn=_bgn;
         target._end=_end;
         target._storage_end=_storage_end;
       };
};

class T {
  public:
    ...
    void objmove(T&) throw() { /*...*/ };
};

Now, if letting objmove(..) be a move constructor, say ^T(T&),
then MyVect implement may be like the following to move elements.

  T *buf= reinterpret_cast<T*>(::operator new(BufferSize));
  ...
  T *p1=..;   // p1 points to an existing object in buf.
  T *p2=..;   // p2 points somewhere in buf, buf just raw memory.

  new(p2) ^T(*p1);  // move construct object at p2, borrowing
                    // placement new syntax.

Another example for derived class and reference member is:

  class D : public T {
      int _s;
      const int& _rs;
    public:
      ^D(D& d) : ^T(d),_s(d._s),_rs(_s) {};
       // note: the initialization of _rs could be different.
       //       The point here is the constructor knows how and
       //       can initialize referece members.
  };

There are just too many use cases for me to think, I am not skill
at C++, but would like to know your idea, so that I can use
objmove(..) in more confidence. Thank you.

IJ. Wang


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
wij (60)
2/24/2006 4:28:03 PM
comp.lang.c++.moderated 10738 articles. 1 followers. allnor (8509) is leader. Post Follow

20 Replies
638 Views

Similar Articles

[PageSpeed] 28

wij@seed.net.tw wrote:
> Hi:
>   A couple of days ago, I posted a letter "Is swap_void(void*)
> possible?" which is about the move semantics of objects in a
> dynamic array.
> http://groups.google.com.tw/group/comp.lang.c++.moderated/browse_frm/thread/74dfca376425c281/74f636d8724862a7?q=swap_void&rnum=1#74f636d872486
2a7
>
> David Abrahams provided a useful link:
>  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html
> which is a proposal about rvalue reference in C++.

You can also check existing library using this kind of semantics here:

http://upp.sourceforge.net/
http://upp.sourceforge.net/srcdoc$Core$pick_$en-us.html
http://upp.sourceforge.net/srcdoc$Core$PickTypes$en-us.html

Mirek

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Mirek
2/25/2006 11:31:14 AM
On 25 Feb 2006 06:31:14 -0500, Mirek Fidler <cxl@volny.cz> wrote:
>wij@seed.net.tw wrote:
>> David Abrahams provided a useful link:
>>  http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html
>> which is a proposal about rvalue reference in C++.
>
>You can also check existing library using this kind of semantics here:

It's amazing that some people even try to bend the C++ language to
adapt it to their programming paradigm. Instead it would be better to
question the root cause of these attempts, the paradigm, the dogma of
'value samantics'. When the narrow and insufficient generic
programming paradigm ('everything is a value') is abandoned many
'problems' simply disappear. Since the generic programming hype in C++
is over anyway it's time to ask new questions, not to find dubious
answers to dubious questions.

Best regards,
Roland Pibinger

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
rpbg123
2/26/2006 11:58:20 AM
Roland Pibinger wrote:
> On 25 Feb 2006 06:31:14 -0500, Mirek Fidler <cxl@volny.cz> wrote:
> 
>>wij@seed.net.tw wrote:
>>
>>>David Abrahams provided a useful link:
>>> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1690.html
>>>which is a proposal about rvalue reference in C++.
>>
>>You can also check existing library using this kind of semantics here:
> 
> 
> It's amazing that some people even try to bend the C++ language to
> adapt it to their programming paradigm. Instead it would be better to
> question the root cause of these attempts, the paradigm, the dogma of
> 'value samantics'. When the narrow and insufficient generic
> programming paradigm ('everything is a value') is abandoned many
> 'problems' simply disappear.

I agree, but I am afraid you still misunderstand the question.

In my view, all this is about destructors vs. pointers + garbage 
collection (or manual new/delete hell) as resource management tools.

If you are going to abandon "value semantics", you have to replace it 
with something else. One possible soltion is to go "everything is 
reference" Java-like path, in C++ implemented either by introducing 
garbage collection into the language or by using manual deletes (or 
smart pointer semi-automatic).

Or you can start using move semantics, as that is what most of your code 
needs anyway, dramatically increasing the number of classes whose value 
can be transfered, number of algorithms you can apply on them etc.

> Since the generic programming hype in C++ is over anyway.

Says who?

BTW, you are makeing similar claims for quite a long time now. For some 
time I thought that your position on these affairs is similar to mine, 
with minor incompatibilities, however that obviously is not true.

What makes me courious now is what do you really propose?

Using pointers/new/delete? Going back to Object-root based hierarchies 
and typecasts? All I have seen so far is that you "disagree"... :)

Mirek

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Mirek
2/26/2006 4:41:45 PM
Mirek Fidler wrote:
>You can also check existing library using this kind of semantics here:
>
>http://upp.sourceforge.net/
>http://upp.sourceforge.net/srcdoc$Core$pick_$en-us.html
>http://upp.sourceforge.net/srcdoc$Core$PickTypes$en-us.html

Thanks.
Those samples reminded me of using an extra argument, like this:

enum ByMove_t { ByMove };

class T {
  public:
    ...
    T(T&,ByMove_t);
    ...
};

I feel there is different view about object life cycle.
Take swap member function for instance, I'd better like
this implement:

void T::swap(T& t)
{
 char buf[sizeof(T)];
 new(buf) T(t,ByMove);
 new(&t) T(*this,ByMove);
 new(this) T(*((T*)buf),ByMove);
};


IJ. Wang


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
wij
2/26/2006 4:44:19 PM
wij@seed.net.tw wrote:
> Mirek Fidler wrote:
> 
>>You can also check existing library using this kind of semantics here:
>>
>>http://upp.sourceforge.net/
>>http://upp.sourceforge.net/srcdoc$Core$pick_$en-us.html
>>http://upp.sourceforge.net/srcdoc$Core$PickTypes$en-us.html
> 
> 
> Thanks.
> Those samples reminded me of using an extra argument, like this:
> 
> enum ByMove_t { ByMove };
> 
> class T {
>   public:
>     ...
>     T(T&,ByMove_t);
>     ...
> };

Yes, anyway, the main reason why we have ugly pick_ is that it is the 
only way how to get move into the action for return values....

> I feel there is different view about object life cycle.
> Take swap member function for instance, I'd better like
> this implement:
> 
> void T::swap(T& t)
> {
>  char buf[sizeof(T)];
>  new(buf) T(t,ByMove);
>  new(&t) T(*this,ByMove);
>  new(this) T(*((T*)buf),ByMove);
> };
> 

Yes, but what we really want is single generic Swap seamlessly using 
pick transfer for pick-types and normal copy for value-types:

template <class T>
void Swap(T& a, T& b)
{
    T temp = a;
    a = b;
    b = temp;
}

It is true that you have to more precisly define algorithm requirements 
when using implicit move semantics, but it is not as hard as it seems... 
(of course, you cannot use STL algorithms).

Mirek

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Mirek
2/26/2006 7:59:40 PM
On 26 Feb 2006 11:41:45 -0500, Mirek Fidler <cxl@volny.cz> wrote:
>Roland Pibinger wrote:
>> On 25 Feb 2006 06:31:14 -0500, Mirek Fidler <cxl@volny.cz> wrote:
>
>In my view, all this is about destructors vs. pointers + garbage 
>collection (or manual new/delete hell) as resource management tools.

RAII is the way to go. I guess we agree on that. But 'move semantics'
has nothing to do with RAII in the sense of 'deterministic resource
management'.   

>If you are going to abandon "value semantics", you have to replace it 
>with something else. 

I only want to abandon 'value semantics' as the one and only
programming paradigm.

>One possible soltion is to go "everything is 
>reference" Java-like path, in C++ implemented either by introducing 
>garbage collection into the language or by using manual deletes (or 
>smart pointer semi-automatic).

This step is not comprehensible to me. There is no need for GC, manual
deletes and 'smart' pointers. One scope-bound RAII-object can
deterministically manage (create, destroy) an unrestricted number of
objects (polymorhoic or not).  

The distinction between 'objects' and 'values' is the same as between
'entities' and 'attributes' in the Entity-Relationship Model. Objects
have identity, values are used to describe properties of objects. In
similar form this distinctions has beed discovered many times in the
last 2500 or so years.

>Or you can start using move semantics, as that is what most of your code 
>needs anyway, dramatically increasing the number of classes whose value 
>can be transfered, number of algorithms you can apply on them etc.

Only values can be 'moved', not objects.

>> Since the generic programming hype in C++ is over anyway.
>
>Says who?
>
>BTW, you are makeing similar claims for quite a long time now. For some 
>time I thought that your position on these affairs is similar to mine, 
>with minor incompatibilities, however that obviously is not true.

I don't know but probably there is not so much disagreement. 

>What makes me courious now is what do you really propose?
>
>Using pointers/new/delete? Going back to Object-root based hierarchies 
>and typecasts? All I have seen so far is that you "disagree"... :)

IMO, the questions need to be approached at first on the conceptual
level, not on the implementation level. 
There are two aspects intermingled in you answer, the 'objects' vs.
'values' and the resource management question. The major advantage of
C++ is that it 'naturally' enables stack-bound/scope-bound idioms with
deterministic resource management for 'objects'. Resource management
is irrelevant for 'values' or just a question of implementation
optimizations (like the RVO hack and 'move semantics'). If you
restrict yourself to the dogma of 'value semantics' (as most STL
aficionados do) you also restrict your solution space to certain
implementation questions ('How do I avoid unefficient copying'). In
another post you mentioned that you implemented deep copy operations
for your containers of polymorphic objects but just once used them in
your own large code base. Some interesting questions arise form that
observation.   

Best wishes,
Roland Pibinger

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
rpbg123
2/26/2006 8:05:40 PM
Roland Pibinger wrote:
> On 26 Feb 2006 11:41:45 -0500, Mirek Fidler <cxl@volny.cz> wrote:
> 
>>Roland Pibinger wrote:
>>
>>>On 25 Feb 2006 06:31:14 -0500, Mirek Fidler <cxl@volny.cz> wrote:
>>
>>In my view, all this is about destructors vs. pointers + garbage 
>>collection (or manual new/delete hell) as resource management tools.
> 
> 
> RAII is the way to go. I guess we agree on that.
Agree... (but I acutally think this is not a good name, "Is 
Initialization" part is confusing, e.g.

File f;
f.Open(...); // is this initialization?

I think it should be rather called "destructor is always responsible for 
resource cleanup" (DIARFRC? :) or something like that).


> But 'move semantics'
> has nothing to do with RAII in the sense of 'deterministic resource
> management'.   

Well, yes and no. Move allows you to pass the instance from one scope to 
another. This is something that is really needed quite often.

And it still is very deterministic, unlike reference counted smart 
pointers. There is always only one scope (I mean, one destructing '}') 
to which the instance belongs. Move just moves instance from one '}' to 
another (usually outer), but always in deterministic way.

>>One possible soltion is to go "everything is 
>>reference" Java-like path, in C++ implemented either by introducing 
>>garbage collection into the language or by using manual deletes (or 
>>smart pointer semi-automatic).
> 
> 
> This step is not comprehensible to me. There is no need for GC, manual
> deletes and 'smart' pointers.

Exactly (sometimes take a closer look, U++ has about 200 000 lines and 
70 deletes in them without using shared smart pointers).

> One scope-bound RAII-object can
> deterministically manage (create, destroy) an unrestricted number of
> objects (polymorhoic or not).  

Yes. The only problem is that sometimes it is very useful to move it to 
another scope.

Actually, you could probably get away without move in many cases, but 
not always, and avoiding moves would make your code more complex and 
less maintainable.

>>Or you can start using move semantics, as that is what most of your code 
>>needs anyway, dramatically increasing the number of classes whose value 
>>can be transfered, number of algorithms you can apply on them etc.
> 
> 
> Only values can be 'moved', not objects.

Just terminology issue. I prefer clean code with simple maintainance to 
philosophy.

>>Using pointers/new/delete? Going back to Object-root based hierarchies 
>>and typecasts? All I have seen so far is that you "disagree"... :)
> 
> 
> IMO, the questions need to be approached at first on the conceptual
> level, not on the implementation level. 
> There are two aspects intermingled in you answer, the 'objects' vs.
> 'values' and the resource management question. The major advantage of
> C++ is that it 'naturally' enables stack-bound/scope-bound idioms with
> deterministic resource management for 'objects'.

Agree. That part is unique for C++! It is actually the main reason for 
using it.

> If you
> restrict yourself to the dogma of 'value semantics' (as most STL
> aficionados do) you also restrict your solution space to certain
> implementation questions ('How do I avoid unefficient copying'). In
> another post you mentioned that you implemented deep copy operations
> for your containers of polymorphic objects but just once used them in
> your own large code base. Some interesting questions arise form that
> observation.

Actually, yes, this is a VERY CORRECT observation!!!

OTOH, I am using pick (implicit move) operation VERY often. It is the 
most natural semantics for transfering the "content" of containers. Most 
problems simply go away when it is default for containers - resources 
are still managed in deterministic way, it is effective and surprisingly 
sometimes even less error-prone than copying containers.

Just a note, while "pick" is general concept, in reality, over time, it 
proved to be useful mainly for containers. I do not think it is a good 
idea to add move/pick semantics to any type with resource handle. 
However it is always good to be able to pass around any object instance 
stored in owning/picking container.

Mirek

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Mirek
2/27/2006 8:03:22 AM
Mirek Fidler wrote:
>...
>Yes, but what we really want is single generic Swap seamlessly using
>pick transfer for pick-types and normal copy for value-types:
>
>template <class T>
>void Swap(T& a, T& b)
>{
>    T temp = a;
>    a = b;
>    b = temp;
>
>}
>
>It is true that you have to more precisly define algorithm requirements
>when using implicit move semantics, but it is not as hard as it seems...
>(of course, you cannot use STL algorithms).

The posted Swap example confused me. Will you introduce the
pick-type and intent?

I don't figure out why a move constructor can be explicitly
invoked, instead of by new.

IJ. Wang


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
wij
2/27/2006 4:19:30 PM
wij@seed.net.tw wrote:
> Mirek Fidler wrote:
> 
>>...
>>Yes, but what we really want is single generic Swap seamlessly using
>>pick transfer for pick-types and normal copy for value-types:
>>
>>template <class T>
>>void Swap(T& a, T& b)
>>{
>>   T temp = a;
>>   a = b;
>>   b = temp;
>>
>>}
>>
>>It is true that you have to more precisly define algorithm requirements
>>when using implicit move semantics, but it is not as hard as it seems...
>>(of course, you cannot use STL algorithms).
> 
> 
> The posted Swap example confused me. Will you introduce the
> pick-type and intent?

In short, pick-type is a type that has regular copy constructor and 
operator= moving (that is, destroys the source).

I hope explanation given here is good enough:

http://upp.sourceforge.net/srcdoc$Core$pick_$en-us.html

Mirek

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Mirek
2/27/2006 8:00:15 PM
On 27 Feb 2006 03:03:22 -0500, Mirek Fidler <cxl@volny.cz> wrote:
>Roland Pibinger wrote:
>> One scope-bound RAII-object can
>> deterministically manage (create, destroy) an unrestricted number of
>> objects (polymorhoic or not).  
>
>Yes. The only problem is that sometimes it is very useful to move it to 
>another scope. Actually, you could probably get away without move in 
>many cases, but not always, and avoiding moves would make your code 
>more complex and less maintainable.

IMO, resources (dynamically created objects are resources) should be
bound to one and only one scope. This is the most viable way for
deterministic resource management.

>> Only values can be 'moved', not objects.
>
>Just terminology issue. I prefer clean code with simple maintainance to 
>philosophy.

No, it's a fundamental issue and it seems to be the 'blind spot' in
your system.
If you don't acknowledge the difference between 'values' and 'objects'
and stick to the 'everything is a value' paradigm then all is just a
question of low-level optimizations. Values are not resources and need
not be managed. Objects have identity (in C/C++ represented at first
by the physical address), are non-copyable, and appear as resources if
created dynamically (some notes on the destinciton between objects and
values can be found at
http://www.two-sdg.demon.co.uk/curbralan/papers/ObjectsOfValue.pdf ).

>OTOH, I am using pick (implicit move) operation VERY often. It is the 
>most natural semantics for transfering the "content" of containers. Most 
>problems simply go away when it is default for containers - resources 
>are still managed in deterministic way, it is effective and surprisingly 
>sometimes even less error-prone than copying containers.

Pick transfer semantics can be mapped entirely to 'usual' C++:

// U++ (http://upp.sourceforge.net/srcdoc$Core$pick_$en-us.html )
IntArray a = MakeArray(100);
IntArray b = a;

// C++
vector<int> a;
MakeArray (a, 100);
vector<int> b;
b.swap(a);

void MakeArray (vector<int>& v, int n) {
  v.resize (n);
  for(int i = 0; i < n; i++) {
     v[i] = i;
  }
}

>Just a note, while "pick" is general concept, in reality, over time, it 
>proved to be useful mainly for containers. I do not think it is a good 
>idea to add move/pick semantics to any type with resource handle. 
>However it is always good to be able to pass around any object instance 
>stored in owning/picking container.

The main benefit for you seems to be the posibility to return a
'shallow copy' of a container "by value". That can be achieved by
other means (see above).

Best regards,
Roland Pibinger 

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
rpbg123
2/27/2006 8:03:28 PM
>>Yes. The only problem is that sometimes it is very useful to move it to 
>>another scope. Actually, you could probably get away without move in 
>>many cases, but not always, and avoiding moves would make your code 
>>more complex and less maintainable.
> 
> 
> IMO, resources (dynamically created objects are resources) should be
> bound to one and only one scope. This is the most viable way for
> deterministic resource management.

Generally, I agree, but sometimes the ability to move owning container 
from one scope to another can significantly reduce the complexity of 
your code.

BTW, just to extend this this theory, there also is a kind of objects 
that are not bound to any scope. E.g. in multiwindow GUI application, it 
is quite reasonable arrangment to have top-level windows "scope-less", 
living on the heap. Typical for such objects is "delete this" operation 
somewhere inside the implementation. However, such objects are rare.

> No, it's a fundamental issue and it seems to be the 'blind spot' in
> your system.
> If you don't acknowledge the difference between 'values' and 'objects'
> and stick to the 'everything is a value' paradigm then all is just a
> question of low-level optimizations.

Well, let me ask a fundamental question:

What is a container? Is it a value or object?

What about owning container?

>>OTOH, I am using pick (implicit move) operation VERY often. It is the 
>>most natural semantics for transfering the "content" of containers. Most 
>>problems simply go away when it is default for containers - resources 
>>are still managed in deterministic way, it is effective and surprisingly 
>>sometimes even less error-prone than copying containers.
> 
> 
> Pick transfer semantics can be mapped entirely to 'usual' C++:

Virtual methods can be mapped entirely to C function pointer tables... 
it is just a little bit more code to write/maintain :)

> // U++ (http://upp.sourceforge.net/srcdoc$Core$pick_$en-us.html )
> IntArray a = MakeArray(100);
> IntArray b = a;
> 
> // C++
> vector<int> a;
> MakeArray (a, 100);
> vector<int> b;
> b.swap(a);

Indeed, using swap to transfer the content of owning container is very 
intuitive :) (well, vector is value container, but we have to base our 
theory on existence of owning container, do not we?)

Looks like you have just replaced one move operation with another...

Mirek

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Mirek
2/28/2006 8:29:34 AM
On 28 Feb 2006 03:29:34 -0500, Mirek Fidler <cxl@volny.cz> wrote:
>
>Well, let me ask a fundamental question:
>
>What is a container? Is it a value or object?

Good question. Since containers are compound objects they take over
the properties of the contained elements. A container of values is a
value, a container of objects is an object.

>> // U++ (http://upp.sourceforge.net/srcdoc$Core$pick_$en-us.html )
>> IntArray a = MakeArray(100);
>> IntArray b = a;
>> 
>> // C++
>> vector<int> a;
>> MakeArray (a, 100);
>> vector<int> b;
>> b.swap(a);
>
>Indeed, using swap to transfer the content of owning container is very 
>intuitive :) 

At least for me 'swap' is very intuitive.

>(well, vector is value container, but we have to base our 
>theory on existence of owning container, do not we?)
>
>Looks like you have just replaced one move operation with another...

An alternative idiom would be to use a handle to enable shallow
copying, eg.

IntArrayHandle MakeArray (int n) {
   IntArray a(n);
   for(int i = 0; i < n; i++) {
      a[i] = i;
   }
   return a.toHandle();
}

IntArray a = MakeArray(100);  // as in U++

Handles are supposed to be copyable therby referencing the same
underlying thing. You need some reference counting for that. But,
unlike 'smart pointers', everything is internal and encapsulated.

Best regards,
Roland Pibinger

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
rpbg123
3/1/2006 11:12:48 AM
Roland Pibinger wrote:
> On 28 Feb 2006 03:29:34 -0500, Mirek Fidler <cxl@volny.cz> wrote:
> 
>>Well, let me ask a fundamental question:
>>
>>What is a container? Is it a value or object?
> 
> 
> Good question. Since containers are compound objects they take over
> the properties of the contained elements. A container of values is a
> value, a container of objects is an object.

Yes, that is the one possible interpretation. However, not the most 
practical one (I am speaking with some 7 years / 500000 C++ lines 
experience here...).

>>>// U++ (http://upp.sourceforge.net/srcdoc$Core$pick_$en-us.html )
>>>IntArray a = MakeArray(100);
>>>IntArray b = a;
>>>
>>>// C++
>>>vector<int> a;
>>>MakeArray (a, 100);
>>>vector<int> b;
>>>b.swap(a);
>>
>>Indeed, using swap to transfer the content of owning container is very 
>>intuitive :) 
> 
> 
> At least for me 'swap' is very intuitive.

BTW, you can always compose swap out of 3 move/copy operations.... 
However, move/copy operation is simply more generic - you can use it for 
wider range of permutational algorithms (e.g. for stable sort).

>>(well, vector is value container, but we have to base our 
>>theory on existence of owning container, do not we?)
>>
>>Looks like you have just replaced one move operation with another...
> 
> 
> An alternative idiom would be to use a handle to enable shallow
> copying, eg.
> 
> IntArrayHandle MakeArray (int n) {
>    IntArray a(n);
>    for(int i = 0; i < n; i++) {
>       a[i] = i;
>    }
>    return a.toHandle();
> }
> 
> IntArray a = MakeArray(100);  // as in U++

Indeed. MOJO. We have of course considered this alternative as well.

Problem is that it does not scale well for composition, re-implementing 
for composite class is quite tricky, much more likely source of bugs 
than implicit moveing for containers (with compiler generated implicit 
operator=/copy-constructor). (You can google for MOJO to see the troubles).

Another trouble is that it makes interface definition somewhat blurry. I 
want MakeArray to return IntArray, not some helper class...

> Handles are supposed to be copyable therby referencing the same
> underlying thing. You need some reference counting for that. But,
> unlike 'smart pointers', everything is internal and encapsulated.

Ops, I must misundertood something... REFERENCE COUNTING? If Handle is 
reference counted, how is it different from the smart pointer? What 
happens here

IntArray a = MakeArray(100);
IntArray b = a;

b[0] = 5;

?

Or is it COW? How it will work with with uncopyable elements?

Mirek

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Mirek
3/1/2006 2:29:42 PM
On 1 Mar 2006 09:29:42 -0500, Mirek Fidler <cxl@volny.cz> wrote:
>Roland Pibinger wrote:
>> 
>> An alternative idiom would be to use a handle to enable shallow
>> copying, eg.
>> 
>> IntArrayHandle MakeArray (int n) {
>>    IntArray a(n);
>>    for(int i = 0; i < n; i++) {
>>       a[i] = i;
>>    }
>>    return a.toHandle();
>> }
>> 
>> IntArray a = MakeArray(100);  // as in U++
>
>Indeed. MOJO. We have of course considered this alternative as well.
>Problem is that it does not scale well for composition, re-implementing 
>for composite class is quite tricky, much more likely source of bugs 
>than implicit moveing for containers (with compiler generated implicit 
>operator=/copy-constructor). (You can google for MOJO to see the troubles).

Actually, what I have in mind is much simpler. More like a file handle
or a Windows handle (but with ref-counting). When you copy a file
handle both handles refer to the same(!) file.

>Another trouble is that it makes interface definition somewhat blurry. I 
>want MakeArray to return IntArray, not some helper class...

With a handle class you can get the desired effect within the usual
C++ semantics and without any language (extension) hacks. Also,
deriving from a base class is not necessary. If you prefer you may
write instad: IntArray a = MakeArrayByHandle(100);

>> Handles are supposed to be copyable therby referencing the same
>> underlying thing. You need some reference counting for that. But,
>> unlike 'smart pointers', everything is internal and encapsulated.
>
>Ops, I must misundertood something... REFERENCE COUNTING? If Handle is 
>reference counted, how is it different from the smart pointer? 

The handle is opaque. Nobody can access the handled object except the
one class that is designated for it. The handle contains no public
member functions except those that make it copyable. Reference
counting produces no overhead in this case (other than the size of the
ref-counter), eg.

class IntArrayHandle {
public:
  IntArrayHandle (const IntArrayHandle& other);
  IntArrayHandle& operator= (const IntArrayHandle& other);
  ~IntArrayHandle();
private:
  friend class IntArray;
  IntArrayHandle();
  IntArrayImp* pImplRefCounted;
};


class IntArray {
public:
  IntArray (IntArrayHandle handle); // refers to the same
                                    // implementation as handle
  IntArrayHandle asHandle()
// ...
private:
  IntArrayImp* pImplRefCounted;
// ...
};
 
>What happens here

>IntArray a = MakeArray(100);
>IntArray b = a;

My proposal is not a complete replacement of what you do in U++. I
prefer not to transfer resources between scopes (see above). It just
demonstrates what you can do within the confines of 'classic' C++. 

IntArray a = MakeArrayByHandle(100);  // copy via handle => shallow
IntArray b = a;  // copy by copy constructor => deep

Best regards,
Roland Pibinger

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
rpbg123
3/1/2006 8:12:46 PM
> The handle is opaque. Nobody can access the handled object except the
> one class that is designated for it. The handle contains no public
> member functions except those that make it copyable. Reference
> counting produces no overhead in this case (other than the size of the
> ref-counter), eg.
> 
> class IntArrayHandle {
> public:
>   IntArrayHandle (const IntArrayHandle& other);
>   IntArrayHandle& operator= (const IntArrayHandle& other);
>   ~IntArrayHandle();
> private:
>   friend class IntArray;
>   IntArrayHandle();
>   IntArrayImp* pImplRefCounted;
> };
> 
> 
> class IntArray {
> public:
>   IntArray (IntArrayHandle handle); // refers to the same
>                                     // implementation as handle
>   IntArrayHandle asHandle()
> // ...
> private:
>   IntArrayImp* pImplRefCounted;
> // ...
> };
>  

Sorry, but this is just a non-template, type-specific smart pointer. 
Calling it a handle and reimplementing access method does not change too 
much. In fact, the only difference I can see is that you are using '.' 
instead of '->'. But you are still left with shared ownership.

>>IntArray a = MakeArray(100);
>>IntArray b = a;
> 
> 
> My proposal is not a complete replacement of what you do in U++. I
> prefer not to transfer resources between scopes (see above). It just
> demonstrates what you can do within the confines of 'classic' C++. 
> 
> IntArray a = MakeArrayByHandle(100);  // copy via handle => shallow
> IntArray b = a;  // copy by copy constructor => deep

Sure you can. You can even stay away from reference counts, as 
demostrated by MOJO.

But as I have explained in previous post, it does not scale with 
composition. And composition is what makes library really productive.

This is BTW that is the fault with most STL tutorials I have seen: They 
are dealing just with trivial element types. However, any non-trivial 
real-world application needs complicated data model and that was where I 
decided to go my way...

Mirek

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Mirek
3/2/2006 8:24:37 AM
Roland Pibinger wrote:
> On 1 Mar 2006 09:29:42 -0500, Mirek Fidler <cxl@volny.cz> wrote:
> >Roland Pibinger wrote:
> >>
> >> An alternative idiom would be to use a handle to enable shallow
> >> copying, eg.
> >>
> >> IntArrayHandle MakeArray (int n) {
> >>    IntArray a(n);
> >>    for(int i = 0; i < n; i++) {
> >>       a[i] = i;
> >>    }
> >>    return a.toHandle();
> >> }
> >>
> >> IntArray a = MakeArray(100);  // as in U++
> >
> >Indeed. MOJO. We have of course considered this alternative as well.
> >Problem is that it does not scale well for composition, re-implementing
> >for composite class is quite tricky, much more likely source of bugs
> >than implicit moveing for containers (with compiler generated implicit
> >operator=/copy-constructor). (You can google for MOJO to see the troubles).
>
> Actually, what I have in mind is much simpler. More like a file handle
> or a Windows handle (but with ref-counting). When you copy a file
> handle both handles refer to the same(!) file.

Sounds a lot like:

struct OpaqueFileStruct;
typedef boost::shared_ptr<OpaqueFileStruct> FileHandle;

FileHandle openFile(const char* path);
void close(FileHandle);
void read(FileHandle, char* buffer, int count);
void write(FileHandle, const char* buffer, int count);

Maybe those ref-counted smart pointers are good for something after
all...

Bob


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Bob
3/2/2006 8:26:41 AM
On 2 Mar 2006 03:24:37 -0500, Mirek Fidler <cxl@volny.cz> wrote:
>
>Sorry, but this is just a non-template, type-specific smart pointer. 

Of course it's not. But I'm not going to convince you of a solution
that I wouldn't use myself. Especially since you have already found
your own special approach. I see that the developement of your library
is already far advanced so that you cannot change a core design
decision any more.

Good luck,
Roland Pibinger


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
rpbg123
3/3/2006 12:32:08 AM
Roland Pibinger wrote:
> On 2 Mar 2006 03:24:37 -0500, Mirek Fidler <cxl@volny.cz> wrote:
> 
>>Sorry, but this is just a non-template, type-specific smart pointer. 
> 
> 
> Of course it's not. But I'm not going to convince you of a solution
> that I wouldn't use myself.

A valid point. Sorry for forgetting that.

Thank you for the nice discussion. I think we both have the same feeling 
for the "deterministic" approach (with the moving exception;)

In fact, I am really glad I can discuss this issue, while I can easily 
express/use determinstic approach in the code, it is a little bit hard 
to explain the concept to public and exchanges like this really help me 
in this respect.

Mirek

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Mirek
3/3/2006 12:43:54 PM
Mirek Fidler wrote:

> OTOH, I am using pick (implicit move) operation VERY often. It is the
> most natural semantics for transfering the "content" of containers. Most
> problems simply go away when it is default for containers - resources
> are still managed in deterministic way, it is effective and surprisingly
> sometimes even less error-prone than copying containers.
>
> Just a note, while "pick" is general concept, in reality, over time, it
> proved to be useful mainly for containers. I do not think it is a good
> idea to add move/pick semantics to any type with resource handle.
> However it is always good to be able to pass around any object instance
> stored in owning/picking container.
>

I found the pick idiom very confusing, just like old unfashioned
auto_ptr's...

When I write an assignment, I want a deep copy, not a move!
When someone reads an assignment, he expects a deep copy, not a move!

If I want a swap, I use a method named swap.
If I want a move, I use a move constructor.
If I want a shallow copy or a shallow assignment, I use a special copy
constructor or a method named shallow!

And almost always, some of these constructs are disallowed, for the
sake of simplicity and clarity. They should be used mainly on
performance bottlenecks / realtime code.

Edson


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
Edson
3/3/2006 7:23:33 PM
Thanks for people have provided helpful links.
This is the workout of the move constructor for dynamic array
(Wy_Array). Note that the purpose of the move constructor is
not for temporary optimization, not for RVO.
Have you any idea?

---------
namespace Wy {
 enum ByMove_t { ByMove };
};

---------
Wy_Array<T> is a class template for managing elements construction
and destruction in a dynamic array, so that valid position refers to
valid element object of type T.

The element class T must be movable by having the move constructor
T(T&,Wy::ByMove_t) defined as described below.

The array is an allocation unit acquired from dynamic store.
Reallocation of the array (and hence the ENOMEM report except from
constructors) occurres only when non-const member operation causes
the resultant array size to exceed the capacity. Thus, array
address is dynamic. If reallocation occurred, the previously output
pointers and references are implicitly undefined.

  Note: Object is predefined the same to the object derived from
        the copy constructor, the term 'same' is implied but not
        specified in the language. For example:

     T a,b;
     Vector<T> v;
     v.push_back(a);   // v[0] is copy constructed from a
     ...               // v.push_back(..)
     v.push_back(b);   // v[0] is the same as was, even though
                       // reallocation may have modified v[0]
                       // by copy constructed again

     Element moving semantics in Wy_Array is defined by the
     move constructor of the element type.

Postcondition for stack unwound by
  Fault : object in equivalent state as the object previously was
  others: object in valid  state (e.g. normally default state or
          equivalent as previously was)

-----------
T::T(T& src, Wy::ByMove_t) throw()      // T is element type

    Make the object src accessible at this pointed address.

    After function completed, src is just regarded non-existant
    without destructor being called and can not be, and the space
    occupied can be recycled. The dummy argument type ByMove_t is
    for function signature, use instance should always be Wy::ByMove.
    This member is supposed to be invoked only via placement new or
    in member initialization list, not to be explicitly called.

    This member is not entirely a real constructor (no new object is
    ever created) but a function in constructor form to support
    object movement in a dynamic array.

    Note: Except in member initialization list, src must be
          a whole type, not reference to a base. Otherwise,
          effect is undefined.

          At final, object src does not exist.

------------
 IJ. Wang


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]

0
wij
3/6/2006 4:22:01 PM
Reply: