Topic: object RELOCATION (MOVE) operation (instead of copy) - for better performance? - in STL?


Author: "Andrei Alexandrescu" <alexandrescua@micromodeling.com>
Date: 1999/03/03
Raw View
Daniel Kaeps wrote in message <36DC0E9F.F8F24C00@no.spam>...
[snip]
>> Another category is formed by objects of variable size, but anyway
no
>> one would want to rely on the compiler to move such objects, so we
can
>> put them aside.
>>
>"objects of variable size" !? What do you mean with that?

You know, the good ol' C trick:

struct Tricky
{
    size_t length;
    char Data[1];
};

// alloc a 11-char array
Tricky * pTricky = (Tricky *)malloc(sizeof(Tricky) + 10);
pTricky->length = 11;

This is nonportable, but used in quite a few places.

>Other syntaxes:
>
>class TSomething
>{
>
>typed version:
> TSomething (/* nonconst */ TSomething  & SourceSomething,
> relocate_t);  // as Dietmar proposed
> void relocateFrom (/* nonconst */ TSomething  & SourceSomething);
>raw memory version:
> void relocateTo (void *DestSomething);
>};


All syntaxes would broke existing code, aI particularly dislike the
one with the method names, which is pretty strange. So far the C++
compiler does not use method names to make decisions on how to deal
with objects.

Andrei
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1999/03/04
Raw View
Andrei Alexandrescu wrote:
>
> Daniel Kaeps wrote in message <36DC0E9F.F8F24C00@no.spam>...
....
> >"objects of variable size" !? What do you mean with that?
>
> You know, the good ol' C trick:
>
> struct Tricky
> {
>     size_t length;
>     char Data[1];
> };
>
> // alloc a 11-char array
> Tricky * pTricky = (Tricky *)malloc(sizeof(Tricky) + 10);
> pTricky->length = 11;
>
> This is nonportable, but used in quite a few places.

Under C9X, this will become portable, with the change that you would
declare:

 char Data[];

with a corresponding change in the relationship between pTricky->length
and the size passed to malloc().


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Daniel Kaeps <please@no.spam>
Date: 1999/03/04
Raw View
Christopher Eltschka schrieb:
[snip]
However, I believe, having a design, that allows a move
operation or move constructor to throw is necessary, because
an move might be implemented via *deep* copy, and thus
*definitely* can throw. This would be also consistent with
the copy-ctors or assignment-operators which can throw too.

[snip]
>
> In modern architectures, there might even be moving support
> to some limit, since the logical address in the program
> doesn't directly reflect the physical address in the system.
> That is, if you object is the only one in a given memory page,
> the virtual memory system can just change the mapping of logical
> pages to "move" the memory to another place, and then allow
> to fix up all pointers inside that relocated memory page
> (this needs intervention from the programmer, since the
> computer doesn't know where the pointers are). No real
> copying would be needed here; just changing some entries in
> the paging table, and some updating of pointers in the
> object itself (since the move was triggered explicitly from
> outside the object, the triggering code is responsible for
> the validity or invalidation of pointers outside of the
> object).
>
That's interesting. Thinking about it a little, I found some
things that would be necessary to regard:

The OS must support the relocating by an application,
because normally an application has no rights to change paging
tables (But I'm not sure).

Systems with paging have (at the moment) normally a *fixed*
page size. If we only put one (or one part of an) object
into one page, we have for some object sizes (e.g. very small
objects) relatively big unused gaps.

Therefore it *might* be necessary to divide into objects
with paging relocation and those with physical relocation
(and e.g. put them on different stacks). OTOH one could use
paging relocation under some special conditions, e.g. if
*all* objects in a page must be moved (this could be the
case in large vectors).

Daniel Kaeps
(kaeps _at_ informatik.uni-leipzig.de)


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Daniel Kaeps <please@no.spam>
Date: 1999/03/04
Raw View
Andrei Alexandrescu schrieb:
>
[variable sized objects]
> struct Tricky
> {
>     size_t length;
>     char Data[1];
> };

OTOH such an Object isn't copyable too (with a normal
operator= or copy-ctor).

However, another interessting issue we have with dynamically
typed objects, e.g. such which come from a object factory.
Personally, I would like to put them on the stack too (not
only on the heap), but therefore will be placement new,
explicit destructor calls and some char[256] or something
like that (either on the normal stack or on an additional
one) necessary. For those one will need something like

 virtual size_t size ();

or something that comes down to the same (e.g. some analogon
to type_id!?).

> >Other syntaxes:
> >
> >class TSomething
> >{
> >
> >typed version:
> > TSomething (/* nonconst */ TSomething  & SourceSomething,
> > relocate_t);  // as Dietmar proposed
> > void relocateFrom (/* nonconst */ TSomething  & SourceSomething);
> >raw memory version:
> > void relocateTo (void *DestSomething);
> >};
>
> All syntaxes would broke existing code, aI particularly dislike the
> one with the method names, which is pretty strange. So far the C++
> compiler does not use method names to make decisions on how to deal
> with objects.
>
They break existing code, but not existing compilers, so for
reasons of compatiblity I would (today) prefer something
that works with todays compilers. But, of course, for the
future, there could be extensions to the compiler too,
especially if this can lead to more performance (and maybe
if there are special conditions that have to be kept, e.g.
like we have such conditions for copy-ctors). OTOH also an
optimizer makes decisions how to deal with objects.

Daniel Kaeps
(kaeps _at_ informatik.uni-leipzig.de)


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/03/05
Raw View
Daniel Kaeps wrote:
>
> Christopher Eltschka schrieb:
> [snip]
> However, I believe, having a design, that allows a move
> operation or move constructor to throw is necessary, because
> an move might be implemented via *deep* copy, and thus
> *definitely* can throw. This would be also consistent with
> the copy-ctors or assignment-operators which can throw too.

I didn't mean that it should be disallowed to throw from the
beginning (after all, even destructors are allowed to throw,
although this can be quite... destructive ;-))

But a swap function using that could be made exception safe
_provided_ the move constructor doesn't throw. Of course,
this is true for the copy constructor even today. But it
is much harder to prevent the copy constructor from throwing
in "move cases", than to write a move-only constructor that
doesn't throw.

And in a concrete project, demanding that a move doesn't
throw will usually be possible since you should generally
be able to move without throwing. OTOH, demanding a copy
constructor that never throws is nearly impossible.

So, if a move constructor goes into a future standard,
it should be allowed to throw; however, one of the advantages
would be that it generally can be made not to throw.

Another interesting point: When should the move constructor
be allowed to be optimized away? One could argue that it
should always be allowed, since conceptionally, the object
at the origin is not valid any more after the move.

For example, on a swap implemented with moves, the
following code

void foo(int&, int&);

void f()
{
  int a=5, b=3;
  swap(a,b);
  foo(a,b);
}

could be optimized by simply making a refer to the address of b
and vice versa, without actually doing anything.

However, if looking at the following code, you can see a
problem with this:

void problem()
{
  int a=3, b=5;
  int* p=&a;
  swap(a, b)
  int* q=&a;
  assert(p==q);
}

With the optimisation, the assertion would fail, because q now
would refer to the address of (now) b.

If a and b were global variables, you obviously couldn't just do
the reinterpretation as well, since you can have code like

int a, b;

void output()
{
  std::cout << a << ", " << b << std::endl;
}

void swaptest()
{
  output();
  swap(a, b);
  output();
}

In this case, pointers to a would continue to point to a.

One would probably need similar rules as for the copy constructor;
however, the copy rules cannot be taken directly, because after
the move, the original object doesn't exist any more.

One can of course just say that moves invalidate pointers and
references, and be done with it - but this would do no good
as well:

template<class T
 void swap(T& t1, T& t2)
{
  T tmp(t1, __move); // oops - reference t1 invalidated!
  new (&t1) T(t2, __move); // address of invalid ref taken!
                           // reference t2 invalidated!
  new (&t2) T(tmp); // and again address of invalid ref taken!
}

So a move constructor is obviously only useful if it doesn't
invalidate pointers and references.

[...]
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Andrei Alexandrescu" <alexandrescua@micromodeling.com>
Date: 1999/03/02
Raw View
Daniel Kaeps wrote in message <36D9B133.85602F8E@no.spam>...
>Any comments in this issue?


Interesting idea. I've also thought of that for a while.

This problem is also related to the lack of a realloc_inplace()
(called _expand() by many implementors who extend the C library).
Because in C there's no function that tries to reallocate a block of
memory while preserving its address, it came naturally that you cannot
implement a kind of 'renew' C++ keyword using C's heap API. If you
could, things could have been more efficient. Now if you want to
reallocate a vector, you have to allocate scratch memory, even though
you could have space at the end of the existing memory block.

Back to the moving subject, there is a category of objects that cannot
be moved by memcpy(): those containing internal pointers. You may
think that those are a minority, but guess what, they include classes
with virtual bases in all C++ implementations that I know of. (Not
that I know of a lot.)

Another category is formed by objects of variable size, but anyway no
one would want to rely on the compiler to move such objects, so we can
put them aside.

What's very interesting, is that the compiler could actually generate
correct code for moving an object, all by itself:

1. Put the source and destination memory range in temporary pointer
variables (Begin, End, BeginDestination)
2. For each primitive member that is not a pointer, memcpy() that
object to the new location
3. Relocate any pointers to virtual bases appropriately
3. For each object that is a pointer, check whether it points
somewhere between Begin and End. If not, memcpy() the pointer. Else,
relocate the pointer by doing simple arithmetic: NewPtr = (OldPtr -
Begin) + BeginDestination
4. For non-primitive members, apply steps 2-4, py passing them the
tuple (Begin, End, BeginDestination) as parameter.

Cases that would defeat this algorithm:

- having internal pointers stored as integral values by using casts
- please name more

As always, I'm eager to propose syntax for this addition. (Proposing
syntaxes for would-be C++ constructs is an exercise that I like. If
I'm annoying anyone, please let me know).

Constructors are too bloated, so let's go in the other direction: what
about a destructor that takes a parameter? This looks quite reasonable
as the object is 'destroyed', yet it passes itself to a follower:

class A()
{
public:
    // normal destructor
    ~A() { ...}
    // moving destructor
    ~A(void *pMemoryToMoveTo)
    {
        // code for moving this to pMemoryToMoveTo
        // default behavior: as noted above
    }
};

Andrei



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Daniel Kaeps <please@no.spam>
Date: 1999/03/02
Raw View
Dietmar Kuehl wrote:
>
> Hi,
> In article <36D9B133.85602F8E@no.spam>,
>   Daniel Kaeps <please@no.spam> wrote:
> > A "relocation" (or maybe "move") will transfer the status of
> > an object from one memory location to another (Destination
> > can be initialized or uninitialized), letting the old
> > location (Source) remaining _uninitialized_ after the move.
>
> I had basically the same idea quite a while ago with the difference that I
> don't want the original object to be "uninitialized" but rather to be
> "destructible": This would mean that eg. all pointer members are transferred
> to the new object and the set to 0 in the original object. This is important
> because the destructor should still be called.

The initialization state I mean signals, whether an object must be
destructed or not. By setting the initialization state to false, the
destructor would not be called.
I see different alternatives to keep track of the initialization state:

1) outside the object: some holder keeps the initialization state of the
object
- e.g. a vector can know while the algorithm of inserting an element,
which elements(memory locations) should be treatened to be initialized
and which not
- implicit (e.g. in the control flow) vs. explicit (in a state
variable).

2) inside the object: the object itself knows, whether it is initialized
(and whether it must be destructed) (the example with pointer=NULL above
would be this type)

Objects that cannot determine, whether they are initialized or not, can
still be used with some "SingleObjectHolder", that will add an explicit
state variable. To neglect the calling of a c++-destructor one would
have to use placement new and explicit destruction.

> This would allow to move
> objects from the stack to some place else. I didn't push this thing because
> there was something which was suitable close, namely the swap function:
> Instead of really relocating the object, it was sufficient for my application
> to just 'swap()' the object.

Because relocating seems to be more fundamental than swap, I would
rather do a design that bases swap on relocation.
I see the main advantage in moving objects on the STACK around, and if
possible to avoid using the heap. E.g. to return objects (the return
value or output arguments) created in a function to the caller by
relocation, instead of copy.

> However, I have also implemented an array class
> which makes more aggressive use of relocation. I still think that it would be
> a good idea to define "relocatable" properties which would mainly consist of
> something like a special copy constructor (eg. marked with a dummy argument
> 'relocate') which transfers internal resources. Using this constructor would
> only be allowed in case that the original is destructed immediately after
> this use.

Somehow there was a issue that allows a compiler to *eliminate* calls to
copy-constructors, I'm not sure what are the consequences of this issue,
if copy-constructors were used for relocation. (but if the copy-ctors
are not "chained" (nested) then there should be no eliminating of the
copy-ctors by the compiler.)

> Another reason why I didn't investigate relocation further was that I decided
> that  it would be sufficient to make use of smart pointer classes: These are
> quite cheap to copy. The only problem is that this introduces another
> indirection but this is most of the times acceptable. Where it is not
> acceptable, it is questionable whether relocation is acceptable...
>
I see the advantage of relocation over deep copy even for smart
pointers, because in case of relocation incrementing/decrementing the
reference counter will be not necessary.

> In cases where copying objects is expensive but construction is relatively
> cheap, it might be reasonable to implement the function 'swap()' (it would be
> reasonable independent from relocation because 'swap()' is eg. used by the
> 'sort()' algorithms). Potentially this function could be used during
> relocation... It probably is not because this would mean that there is a
> default constructor for these objects...
>

If such a basic design is not considered from the beginning, its
difficult to introduce it, if already tons of code exist (algorithms
will have to be changed, than they must be retested etc.). Maybe at the
moment its implicit everywhere, but explicit nowhere.

And if the design will be part of the STL in the future, then its
probably a good idea to start implementing new classes with the new
design from the present on. As a workaround it should be possible to
emulate ANY relocaton by a deepcopy + (explicit) destructor call to the
source.

Relocation also exists for relocating between different adress spaces
e.g. in the distributed programming (there called "migration"). But e.g.
CORBA does not adress relocation from the beginning on (unlike DC++, a
research project, which adresses this and familiar issues very deep),
and I'm a bit angry that CORBA implementators will restart from scratch
to build this in).

Daniel Kaeps
(kaeps _at_ informatik.uni-leipzig.de)


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: markw65@my-dejanews.com
Date: 1999/03/03
Raw View
In article <36db39b4.0@10.1.1.65>,
  "Andrei Alexandrescu" <alexandrescua@micromodeling.com> wrote:
>
> Back to the moving subject, there is a category of objects that cannot
> be moved by memcpy(): those containing internal pointers. You may
> think that those are a minority, but guess what, they include classes
> with virtual bases in all C++ implementations that I know of. (Not
> that I know of a lot.)

I have seen an implementation which parallels virtual function tables. ie a
class with virtual bases has a pointer to a static table of offsets. Saves
space in the object, as well as leaving it relocatable.

Mark Williams

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Daniel Kaeps <please@no.spam>
Date: 1999/03/03
Raw View
Andrei Alexandrescu wrote:
>
[snip of realloc discussion]
>
Yes, reallocation would also be good in conjunction with the relocation
operation.

> Back to the moving subject, there is a category of objects that cannot
> be moved by memcpy(): those containing internal pointers. You may
> think that those are a minority, but guess what, they include classes
> with virtual bases in all C++ implementations that I know of. (Not
> that I know of a lot.)
>
For such classes a memcpy cannot be made for sizeof(object).
But one can do placement new (or copy-construct) an object of the same
type as source, an than to call a function "relocateFrom()", that
shallow-copies only the member variables, but *not* the pointer to a
VTABL(or a common base objectpart pointer). In case of a virtual base
class one has to ensure somehow, that the common base objectpart is not
relocated *twice*.

Conceptual I see objects that or not copyable, only relocatable (e.g
such that are single owners of non copyable objects on the heap).

> Another category is formed by objects of variable size, but anyway no
> one would want to rely on the compiler to move such objects, so we can
> put them aside.
>
"objects of variable size" !? What do you mean with that?

> What's very interesting, is that the compiler could actually generate
> correct code for moving an object, all by itself:
>
[snip of relocation algorithm]

Relocating should still be a user defineable operation, e.g. to allow
programs to keep referential integrity even for pointers that come from
outside an object into the object (and not only from the object itself).

Compiler support for relocating - maybe - e.g. an object relocation
supported by the compiler might by faster than placement new. (But this
might be even true for (deep)copying objects, not only moving).
>
> Cases that would defeat this algorithm:
>
> - having internal pointers stored as integral values by using casts
>
- e.g. same reasons that dont allow a (conceptual)relocation implemented
by a deep copy (e.g. in most vector implementations): Pointers from
outside into or to an object.

> As always, I'm eager to propose syntax for this addition. (Proposing
> syntaxes for would-be C++ constructs is an exercise that I like. If
> I'm annoying anyone, please let me know).
>
[snip destructor syntax]

The disadvantage of these destructors will be, that you need compiler
support for it...

Other syntaxes:

class TSomething
{

typed version:
 TSomething (/* nonconst */ TSomething  & SourceSomething,
  relocate_t);  // as Dietmar proposed
 void relocateFrom (/* nonconst */ TSomething  & SourceSomething);
raw memory version:
 void relocateTo (void *DestSomething);
};

Daniel Kaeps
(kaeps _at_ informatik.uni-leipzig.de)
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: AllanW@my-dejanews.com
Date: 1999/03/03
Raw View
In article <36db39b4.0@10.1.1.65>,
  "Andrei Alexandrescu" <alexandrescua@micromodeling.com> wrote:
> What's very interesting, is that the compiler could actually generate
> correct code for moving an object, all by itself:
>
> 1. Put the source and destination memory range in temporary pointer
> variables (Begin, End, BeginDestination)
> 2. For each primitive member that is not a pointer, memcpy() that
> object to the new location
> 3. Relocate any pointers to virtual bases appropriately
> 3. For each object that is a pointer, check whether it points
> somewhere between Begin and End. If not, memcpy() the pointer. Else,
> relocate the pointer by doing simple arithmetic: NewPtr = (OldPtr -
> Begin) + BeginDestination
> 4. For non-primitive members, apply steps 2-4, py passing them the
> tuple (Begin, End, BeginDestination) as parameter.
>
> Cases that would defeat this algorithm:
>
> - having internal pointers stored as integral values by using casts
> - please name more

  - Pointers which are part of a union with non-pointer types, i.e.
        union { char*charptr; double dbl; } x; };
  - Data allocated by malloc() and used for ??? possibly graphics data,
        but also possibly an array of structures which contain pointers
  - Non-standard pointer types, i.e.
        "Near" or "Based" pointers on 16-bit MSDOS
  - Pointers which are intentionally offset by some constant (terrible
        technique, but not unheard of, already illegal I think but
        it usually works...)

----
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1999/03/03
Raw View
Daniel Kaeps wrote:
>
> Hi!
>
> Somehow, thinking about usage patterns for objects in
> containers and arrays I found out, that it would be quite
> wise to have an operation (or function if one wants) called
> "relocation".
>
> A "relocation" (or maybe "move") will transfer the status of
> an object from one memory location to another (Destination
> can be initialized or uninitialized), letting the old
> location (Source) remaining _uninitialized_ after the move.

See f.ex. the thread "buldozer_copy" ?  last november in this
newsgroup for some previous discussion.

I'd like to have not a function, but a "move constructor",
which would then also be used by the compiler for things
like function return.
The compiler-generated move constructor would just call the
copy constructor. Classes which can do better (f.ex.
with shallow-copy), simply provide their own move constructor.
The use of a move constructor would cause no incompatibilities
with old code, since all code written up to date doesn't define
a move constructor, and therefore the compiler-generated move
constructor would be called, which just calls the copy constructor,
which is called today as well.
Explicit usage of move constructors also has advantages:
Unlike a copy constructor, a move constructor can in most
cases be written in a way that doesn't throw. With non-throwing
move constructors, an exception-safe swap could be implemented
easily:

template<class T>
 void swap(T& a, T& b)
{
  T tmp(a, __move);       // move a to tmp
  new(&a) T(b, __move);   // move b to a
  new(&b) T(tmp, __move); // move tmp to b
}

I think this can even be implemented as compiler extension
(which would consist of auto-generating a move constructor
and using that instead of the copy for "moving" operations
where the source object is no longer used).
A conforming program cannot tell, since it won't use __move.

Of course, a generic move function would give at least
objects (esp. containers) a possibbility for optimisation.
However, with a moving constructor, the compiler itself
could optimize in such situations.

>
> In reality, moving objects seems to be more fundamentally
> then copying. I dont clone myself when I change my position
> in space (deleting my original(me?) after copying)! (But fun
> off, moving will be implemented by a shallow _copy_
> (hardware moving is not supported I think)).

In modern architectures, there might even be moving support
to some limit, since the logical address in the program
doesn't directly reflect the physical address in the system.
That is, if you object is the only one in a given memory page,
the virtual memory system can just change the mapping of logical
pages to "move" the memory to another place, and then allow
to fix up all pointers inside that relocated memory page
(this needs intervention from the programmer, since the
computer doesn't know where the pointers are). No real
copying would be needed here; just changing some entries in
the paging table, and some updating of pointers in the
object itself (since the move was triggered explicitly from
outside the object, the triggering code is responsible for
the validity or invalidation of pointers outside of the
object).

[...]


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1999/03/03
Raw View

> > In article <36D9B133.85602F8E@no.spam>,
> >   Daniel Kaeps <please@no.spam> wrote:
> > > A "relocation" (or maybe "move") will transfer the status of
> > > an object from one memory location to another (Destination
> > > can be initialized or uninitialized), letting the old
> > > location (Source) remaining _uninitialized_ after the move.

> Dietmar Kuehl wrote:
> > I had basically the same idea quite a while ago with the difference that I
> > don't want the original object to be "uninitialized" but rather to be
> > "destructible": This would mean that eg. all pointer members are transferred
> > to the new object and the set to 0 in the original object. This is important
> > because the destructor should still be called.

> The initialization state I mean signals, whether an object must be
> destructed or not. By setting the initialization state to false, the
> destructor would not be called.

We're talking about an alternative to constructing one object and
destructing another one. The compiler already knows one way to do this
(first, use the copy constructor and then use the destructor), but we
want to allow the programmer to streamline the process.

It seems to me that this calls for a new "special member function."
Something like this (though syntax could vary):

    char *strdup(const char*c)
    { return c ? strcpy(new char[strlen(c)+1],c) : c; }

    class myBaseClass {
    protected:
        char *name;
        // ...
    public:
        myBaseClass() : name(0) {}
        explicit myBaseClass(const char*c) : name(strdup(c)) {}
        myBaseClass(const myBaseClass&c) : name(strdup(c.name)) {}
        myBaseClass&operator=(const char*c)
            { delete[] name; name=strdup(c); return *this; }
        myBaseClass&operator=(const myBaseClass&c)
            { delete[] name; name=strdup(c.name); return *this; }
        ~myBaseClass() { delete name; }
        // See version in myClass, below ... commented better
        @myBaseClass(myBaseClass&c) : name(c.name) {}
    };

    class myClass : public myBaseClass {
        char *addr;
        char *ph;
        // ... etc ...
    public:

        // Here is the default constructor
        myClass() : myBaseClass(0), addr(0), ph(0) {}

        // Here is the copy constructor
        myClass(const myClass&c)
            : myBaseClass(c.name)
            , addr(strdup(c.addr))
            , ph  (strdup(c.ph  ))
            {}
        // Note that if we hadn't written this, the compiler-created
        // default would have been equivalent to
        //myClass(const myClass&c) : myBaseClass(c), addr(c.addr), ph(c.ph) {}
        // which in this case would have been very wrong.

        // Here is the normal destructor. Note that myBaseClass
        // destructor is called as soon as we're complete here.
        ~myClass() { delete[] addr; delete[]ph; }

        // Here is the assignment operator
        myClass &operator=(const myClass&c) {
            delete[] name; name=strdup(c.name);
            delete[] addr; addr=strdup(c.addr);
            delete[] ph;   ph  =strdup(c.ph);
            return *this;
        }
        // Note that if we hadn't written this, the compiler-created
        // default would have been equivalent to
        //myClass&operator=(const myClass&c)
        //{name=c.name; addr=c.addr; ph=c.ph; return *this;}
        // which in this case would have been very wrong.

        // Here is the new "move constructor"
        // As soon as this is complete, c is considered destructed
        // Note that this is the first-ever alternative to the destructor
        @myClass(myClass&c) : myBaseClass(c.name), addr(c.addr), ph(c.ph) {}
        // Note that if we hadn't written this, the compiler-created
        // default would have been equivalent to calling the copy
        // constructor, and then calling the destructor for c.
        // The default *would* work correctly, so long as the copy
        // constructor and destructor work correctly.

        // No reason we can't overload, for simultaneous conversion
        // and movement:
        @myClass(myBaseClass&c) : myBaseClass(c.name), addr(0), ph(0) {}

        // Other member functions...
    };

Just as ~ signifies a destructor rather than a constructor, @ would
signify a move (construct/destruct pair). Note that the argument
must be a non-const reference, because the function is destructive.
Alternatives to the @ character would include $ or ~~. As shown
above, overloading would be possible. The semantics of "explicit"
would always be assumed, so the keyword would not be required or
allowed.

I have thought this through some. One possible problem is what to
do with derived classes:

    class myDerivedClass : public myClass {
        char *shipaddr;
    public:
        ~myClass() {
            // Shipping address might have been copied from address:
            if (shipaddr != addr) {
                // It's different, so it's safe to delete
                delete[] shipaddr;
                shipaddr = 0;
            }
            // Make sure it was really deleted
            delete addr;
            addr = 0;
        }
    };
    myClass xxx() {
        myDerivedClass derivedObject;
        // ... manipulate derivedObject;
        return derivedObject;
    }

Here we call myDerivedClass::~myDerivedClass() to delete the
myDerivedClass members (shipaddr), leaving the myClass sub-object
intact. Then we call myClass::@myClass(myClass&c) to move the
remaining members to the new myClass object. That probably works
reasonably well, but I'm not in love with it. In the example above,
the destructor for myDerivedClass assumes that addr will be deleted
very soon anyway, and that there's no harm in doing so early.
But this will now cause the copied myClass sub-object to behave
differently than it otherwise would have.

----
AllanW@my-dejanews.com is a "Spam Magnet" -- never read.
Please reply in USENET only, sorry.

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Daniel Kaeps <please@no.spam>
Date: 1999/02/28
Raw View
Hi!

Somehow, thinking about usage patterns for objects in
containers and arrays I found out, that it would be quite
wise to have an operation (or function if one wants) called
"relocation".

A "relocation" (or maybe "move") will transfer the status of
an object from one memory location to another (Destination
can be initialized or uninitialized), letting the old
location (Source) remaining _uninitialized_ after the move.

In reality, moving objects seems to be more fundamentally
then copying. I dont clone myself when I change my position
in space (deleting my original(me?) after copying)! (But fun
off, moving will be implemented by a shallow _copy_
(hardware moving is not supported I think)).

The ADVANTAGE of a relocation over a copy would be, that
e.g. to insert an element in the middle of a vector there
will be no copy operations (which are VERY expensive in case
that the copied object owns objects on the heap).

It seems to me, that the current standard library does not
encourage the "relocation-design" - unfortunately. Is such a
design considered for future STL standards?

I've looked in DejaNews an suprisingly the first occurence
of such an idea is in 1996 (or maybe it was '95). But
somehow it has been more or less ignored. Why?

Any comments in this issue?

Thanks...

Daniel Kaeps
(kaeps _at_ informatik.uni-leipzig.de)


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Dietmar Kuehl <dietmar.kuehl@claas-solutions.de>
Date: 1999/03/01
Raw View
Hi,
In article <36D9B133.85602F8E@no.spam>,
  Daniel Kaeps <please@no.spam> wrote:
> A "relocation" (or maybe "move") will transfer the status of
> an object from one memory location to another (Destination
> can be initialized or uninitialized), letting the old
> location (Source) remaining _uninitialized_ after the move.

I had basically the same idea quite a while ago with the difference that I
don't want the original object to be "uninitialized" but rather to be
"destructible": This would mean that eg. all pointer members are transferred
to the new object and the set to 0 in the original object. This is important
because the destructor should still be called. This would allow to move
objects from the stack to some place else. I didn't push this thing because
there was something which was suitable close, namely the swap function:
Instead of really relocating the object, it was sufficient for my application
to just 'swap()' the object. However, I have also implemented an array class
which makes more aggressive use of relocation. I still think that it would be
a good idea to define "relocatable" properties which would mainly consist of
something like a special copy constructor (eg. marked with a dummy argument
'relocate') which transfers internal resources. Using this constructor would
only be allowed in case that the original is destructed immediately after
this use.

Another reason why I didn't investigate relocation further was that I decided
that  it would be sufficient to make use of smart pointer classes: These are
quite cheap to copy. The only problem is that this introduces another
indirection but this is most of the times acceptable. Where it is not
acceptable, it is questionable whether relocation is acceptable...

> The ADVANTAGE of a relocation over a copy would be, that
> e.g. to insert an element in the middle of a vector there
> will be no copy operations (which are VERY expensive in case
> that the copied object owns objects on the heap).

In cases where copying objects is expensive but construction is relatively
cheap, it might be reasonable to implement the function 'swap()' (it would be
reasonable independent from relocation because 'swap()' is eg. used by the
'sort()' algorithms). Potentially this function could be used during
relocation... It probably is not because this would mean that there is a
default constructor for these objects...

> I've looked in DejaNews an suprisingly the first occurence
> of such an idea is in 1996 (or maybe it was '95). But
> somehow it has been more or less ignored. Why?

The committee was busy fixing real problems until very recently. Since smart
pointers are a reasonable approach to this problem, I guess it was not
considred to be that efficient. Maybe Alexander Stepanov can comment why he
didn't consider it in his first design: After all he considers efficiency to
be very important and relocation can improve performance in at least some
situations... -- <mailto:dietmar.kuehl@claas-solutions.de> homepage:
<http://www.informatik.uni-konstanz.de/~kuehl>

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Andrei Alexandrescu" <alexandrescua@micromodeling.com>
Date: 1999/03/02
Raw View
Dietmar Kuehl wrote in message <7bf3iu$a15$1@nnrp1.dejanews.com>...
>Maybe Alexander Stepanov can comment why he
>didn't consider it in his first design: After all he considers efficiency to
>be very important and relocation can improve performance in at least some
>situations...

Unfortunately, according to DejaNews archives, the last message
Stepanov wrote on the USENET was at the end of a lengthy discussion
with a couple of guys on comp.lang.scheme.
During the discussion, by the way, I've read *great* points Stepanov
has made. You may want to use DejaNews' power search to see them -
it's well worth reading them.

I said "unfortunately" because in the end, exasperated, responding to
a rude attack (you know how those unmoderated newsgroups are like),
Stepanov said something like: "The newsgroups are for people like you.
I promise never to post a message on the Usenet again". And that's
what he did.

Is there anyone who reads this and knows him? Maybe he could be
convinced to join comp.std.c++ and comp.lang.c++.moderated. It would
be great - I've got lots of insights by reading his work.

Andrei
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]