How to "reset" an object

V

Virchanza

Let's say I have a struct as follows:

Code:
struct MyStruct{

    int a;

    double b;

    std::string str;

};

And let's say I create a global object of it which has all of its
members (including the intrinsic-type members) default-initialised:

Code:
MyStruct g_obj = MyStruct();

Half-way through my program's execution, I want to "reset" this global
object. By "reset", I mean I want all of its member to go back to the
state that they were in when the "g_obj" object was initially created.
I achieve this with the following function:

Code:
template<class T>
void ResetObject(T &obj)
{
    obj.~T();  /* Destruct */

   ::new(&obj) T();  /* Construct */
}

This works fine for a struct/class object. However, I want this
template function to work for any kind of object, be it an intrinsic
or an array.

The problem with intrinsics or arrays is that you can't invoke their
destructor with the usual syntax (hence my template function will give
a compiler error).

Here's an idea I have in mind:

Code:
template<class T>
void ResetObject(T &obj)
{
    struct Container {
        T member;
    };

    Container *const p = reinterpret_cast<Container*>(&obj);

    p->~Container();

   ::new(p) Container();
}

The Standard has the following to say:

A pointer to a POD-struct object, suitably converted using a
reinterpret_cast, points to its initial
member (or if that member is a bit-field, then to the unit in which it
resides) and vice versa. [Note: There might therefore be unnamed
padding within a POD-struct object, but not at its beginning, as
necessary to achieve appropriate alignment. ]"

The above quote only applies to POD-struct objects. If my Container
class contains a non-POD member, then the Standard doesn't guarantee
that the address of a Container object is the same as the address of
its first member. However, given that my Container class only contains
1 member, it's quite likely that the two addresses will be equal.

The only way I can imagine my template function behaving unexpectedly
is if the implementation stores some sort of "hidden data" within the
Container type (perhaps some debugging information). Possible
scenarios are:
1) Padding or debugging information at the beginning of the Container
(which is permitted by the Standard if the Container Class is a non-
POD)
2) Padding or debugging information at the end of the Container (which
is permitted by the Standard for both POD's and non-POD's)

Anyway...

Anyone got any ideas on how to Reset any kind of object?

Any opinions?
 
A

Alf P. Steinbach

* Virchanza, on 19.06.2010 11:58:
Anyone got any ideas on how to Reset any kind of object?

Assign it to it.

If it doesn't support assignment then it's designed to not support it.

In that case trying to circumvent the design is generally misguided.


Cheers & hth.,

- Alf

PS: The FAQ has some good advice.
 
F

Francesco S. Carta

Virchanza said:
Let's say I have a struct as follows:

Code:
struct MyStruct{

    int a;

    double b;

    std::string str;

};

And let's say I create a global object of it which has all of its
members (including the intrinsic-type members) default-initialised:

Code:
MyStruct g_obj = MyStruct();

Half-way through my program's execution, I want to "reset" this global
object. By "reset", I mean I want all of its member to go back to the
state that they were in when the "g_obj" object was initially created.
I achieve this with the following function:

Code:
template<class T>
void ResetObject(T &obj)
{
    obj.~T();  /* Destruct */

   ::new(&obj) T();  /* Construct */}

This works fine for a struct/class object. However, I want this
template function to work for any kind of object, be it an intrinsic
or an array.

The problem with intrinsics or arrays is that you can't invoke their
destructor with the usual syntax (hence my template function will give
a compiler error).

Here's an idea I have in mind:

Code:
template<class T>
void ResetObject(T &obj)
{
    struct Container {
        T member;
    };

    Container *const p = reinterpret_cast<Container*>(&obj);

    p->~Container();

   ::new(p) Container();}

The Standard has the following to say:

A pointer to a POD-struct object, suitably converted using a
reinterpret_cast, points to its initial
member (or if that member is a bit-field, then to the unit in which it
resides) and vice versa. [Note: There might therefore be unnamed
padding within a POD-struct object, but not at its beginning, as
necessary to achieve appropriate alignment. ]"

The above quote only applies to POD-struct objects. If my Container
class contains a non-POD member, then the Standard doesn't guarantee
that the address of a Container object is the same as the address of
its first member. However, given that my Container class only contains
1 member, it's quite likely that the two addresses will be equal.

The only way I can imagine my template function behaving unexpectedly
is if the implementation stores some sort of "hidden data" within the
Container type (perhaps some debugging information). Possible
scenarios are:
1) Padding or debugging information at the beginning of the Container
(which is permitted by the Standard if the Container Class is a non-
POD)
2) Padding or debugging information at the end of the Container (which
is permitted by the Standard for both POD's and non-POD's)

Anyway...

Anyone got any ideas on how to Reset any kind of object?

Any opinions?

template<typename T> void reset(T& t) {
t = T();
}

You shouldn't need anything more than that.

Don't fiddle with calling destructors unless you know very well what
you're doing, read the FAQ about this subject before ever calling one
of them, and even when you think you really should call them, think
twice about it.
 
V

Virchanza

Okey dokey I think I have a solution that's guaranteed to work. (Or at
the very least, it will fail to compile on the systems where it won't
work).

Hopefully the comments in my code are enough to explain what I'm
getting at:

Code:
#include <new>    /* Placement new */

template <typename T>
void ResetObject(T *const p_obj)  /* p_obj = Address of object to
reset */
{
    struct Container {
        T member;
    };


    /*========================================================
        The line of code that follows guarantees two things:

           1) The address of a Container object is the
              same as the address of its first member

           2) The compiler doesn't store any extra data
              inside the Container type (e.g. debugging data)

    ========================================================== */

    typedef int Compile_Time_Assert[ (sizeof(Container) ==
sizeof(T)) ? 2 : -1 ];


    Container *const p_container = reinterpret_cast<Container
*>(p_obj);

    p_container->~Container();    /* Destruct the object */

    ::new(p_container) Container();    /* Construct the object
(parentheses make it default-initialised) */
}

/* And now just some code to try it out */

#include <iostream>
using std::cout;

struct MyStruct {

    MyStruct()
    {
        cout << "Constructing a MyStruct object\n";
    }

    ~MyStruct()
    {
        cout << "Destructing a MyStruct object\n";
    }
};

int main(void)
{
    cout << "\n--About to construct a single MyStruct object\n";

    MyStruct a;

    int b;

    cout << "\n--About to construct 5 MyStruct objects\n";

    MyStruct arr_a[5];

    int arr_b[5];

    cout << "\n--About to Reset a single MyStruct object\n";

    ResetObject(&a);

    ResetObject(&b);

    cout << "\n--About to Reset 5 MyStruct objects\n";

    ResetObject(&arr_a);

    ResetObject(&arr_b);

    cout << "\nABOUT TO RETURN FROM MAIN\n\n";
}

What do you think?
 
F

Francesco S. Carta

Leigh Johnston said:
Okey dokey I think I have a solution that's guaranteed to work. (Or at
the very least, it will fail to compile on the systems where it won't
work).
Hopefully the comments in my code are enough to explain what I'm
getting at:
Code:
#include <new>    /* Placement new */[/QUOTE]
[QUOTE]
template <typename T>
void ResetObject(T *const p_obj)  /* p_obj = Address of object to
reset */
{
   struct Container {
       T member;
   };[/QUOTE]
[QUOTE]
   /*========================================================
       The line of code that follows guarantees two things:[/QUOTE]
[QUOTE]
          1) The address of a Container object is the
             same as the address of its first member[/QUOTE]
[QUOTE]
          2) The compiler doesn't store any extra data
             inside the Container type (e.g. debugging data)[/QUOTE]
[QUOTE]
   ========================================================== */[/QUOTE]
[QUOTE]
   typedef int Compile_Time_Assert[ (sizeof(Container) ==
sizeof(T)) ? 2 : -1 ];[/QUOTE]
[QUOTE]
   Container *const p_container = reinterpret_cast<Container
*>(p_obj);[/QUOTE]
[QUOTE]
   p_container->~Container();    /* Destruct the object */[/QUOTE]
[QUOTE]
   ::new(p_container) Container();    /* Construct the object
(parentheses make it default-initialised) */
}[/QUOTE]
[QUOTE]
/* And now just some code to try it out */[/QUOTE]
[QUOTE]
#include <iostream>
using std::cout;[/QUOTE]
[QUOTE]
struct MyStruct {[/QUOTE]
[QUOTE]
   MyStruct()
   {
       cout << "Constructing a MyStruct object\n";
   }[/QUOTE]
[QUOTE]
   ~MyStruct()
   {
       cout << "Destructing a MyStruct object\n";
   }
};[/QUOTE]
[QUOTE]
int main(void)
{
   cout << "\n--About to construct a single MyStruct object\n";[/QUOTE]
[QUOTE]
   MyStruct a;[/QUOTE]
[QUOTE]
   int b;[/QUOTE]
[QUOTE]
   cout << "\n--About to construct 5 MyStruct objects\n";[/QUOTE]
[QUOTE]
   MyStruct arr_a[5];[/QUOTE]
[QUOTE]
   int arr_b[5];[/QUOTE]
[QUOTE]
   cout << "\n--About to Reset a single MyStruct object\n";[/QUOTE]
[QUOTE]
   ResetObject(&a);[/QUOTE]
[QUOTE]
   ResetObject(&b);[/QUOTE]
[QUOTE]
   cout << "\n--About to Reset 5 MyStruct objects\n";[/QUOTE]
[QUOTE]
   ResetObject(&arr_a);[/QUOTE]
[QUOTE]
   ResetObject(&arr_b);[/QUOTE]
[QUOTE]
   cout << "\nABOUT TO RETURN FROM MAIN\n\n";
}
What do you think?

What if T's constructor(s) throw an exception?  You could be left with
objects that will be destructed twice.  What you suggest is a very ugly hack
indeed which should be used rarely, i.e. providing a general function making
it easy to do is probably a bad idea.

I'm not able to see any use for such a convoluted way of doing
something that simple - therefore I don't understand the reason of
your: "[...] should be used rarely".

The simple template I've posted above...

template<typename T> void reset(T& t) {
t = T();
}

....will work just fine for all the types which are allowed to be
created (without parameters) and assigned.

Anything more than that is either overkilling or dangerous, except in
case the container to be reset owns pointers to objects that must be
destroyed - but in such a case, that container should hold smart
pointers and not raw ones, and my template should work just fine.

Or am I just plain wrong and my previous post has been ignored for
that reason? Please point that out, in such case. I don't like
building my knowledge on wrong assumptions.
 
F

Francesco S. Carta

Leigh Johnston said:
Leigh Johnston said:
Okey dokey I think I have a solution that's guaranteed to work. (Or at
the very least, it will fail to compile on the systems where it won't
work).
Hopefully the comments in my code are enough to explain what I'm
getting at:
Code:
#include <new>    /* Placement new */ 
template <typename T>
void ResetObject(T *const p_obj)  /* p_obj = Address of object to
reset */
{
   struct Container {
       T member;
   }; 
   /*========================================================
       The line of code that follows guarantees two things: 
          1) The address of a Container object is the
             same as the address of its first member 
          2) The compiler doesn't store any extra data
             inside the Container type (e.g. debugging data) 
   ========================================================== */ 
   typedef int Compile_Time_Assert[ (sizeof(Container) ==
sizeof(T)) ? 2 : -1 ]; 
   Container *const p_container = reinterpret_cast<Container
*>(p_obj); 
   p_container->~Container();    /* Destruct the object */ 
   ::new(p_container) Container();    /* Construct the object
(parentheses make it default-initialised) */
} 
/* And now just some code to try it out */ 
#include <iostream>
using std::cout; 
struct MyStruct { 
   MyStruct()
   {
       cout << "Constructing a MyStruct object\n";
   } 
   ~MyStruct()
   {
       cout << "Destructing a MyStruct object\n";
   }
}; 
int main(void)
{
   cout << "\n--About to construct a single MyStruct object\n"; 
   MyStruct a; 
   int b; 
   cout << "\n--About to construct 5 MyStruct objects\n"; 
   MyStruct arr_a[5]; 
   int arr_b[5]; 
   cout << "\n--About to Reset a single MyStruct object\n"; 
   ResetObject(&a); 
   ResetObject(&b); 
   cout << "\n--About to Reset 5 MyStruct objects\n"; 
   ResetObject(&arr_a); 
   ResetObject(&arr_b); 
   cout << "\nABOUT TO RETURN FROM MAIN\n\n";
}
What do you think?
What if T's constructor(s) throw an exception?  You could be left with
objects that will be destructed twice.  What you suggest is a very ugly
hack
indeed which should be used rarely, i.e. providing a general function
making
it easy to do is probably a bad idea.
I'm not able to see any use for such a convoluted way of doing
something that simple - therefore I don't understand the reason of
your: "[...] should be used rarely".
The simple template I've posted above...
template<typename T> void reset(T& t) {
   t = T();
}
...will work just fine for all the types which are allowed to be
created (without parameters) and assigned.
Anything more than that is either overkilling or dangerous, except in
case the container to be reset owns pointers to objects that must be
destroyed - but in such a case, that container should hold smart
pointers and not raw ones, and my template should work just fine.
Or am I just plain wrong and my previous post has been ignored for
that reason? Please point that out, in such case. I don't like
building my knowledge on wrong assumptions.

Your function template is preferable to what the OP suggests yes (yours is
not a hack), however it might be desirable to use destructors and
constructors if for some reason you don't want to write assignment operators
for the class and its members, although I cannot really think of a good
reason for not writing assignment operators if I'm honest.

If that is the only concern, the assignment operators could be made
protected and the reset function could be set as a friend, but once we
are there, I would simply add a reset() member to that class... there
is another thread running on the same subject, I wonder what's the
issue about such a simple task :)

@OP: my template will not work for resetting arrays - of course - and
the suggestion here would be to avoid them. If I need to create
several structures of the same type and store them in some container I
use a standard container such as an std::vector or an std::list.
 
J

Juha Nieminen

Leigh Johnston said:
The only reason I can think of for not wanting to write an assignment
operator is if the compiler generated one is sufficient in which case
assignment to a default constructed object is sufficient for resetting
anyway.

In some cases assignment could be a very heavy operation (at least
unless your compiler already supports rvalue references).

In other cases assignment might not have any meaningful interpretation
with the type in question, as it might be logically impossible to create
copies of an object of a certain type (or even if a logical interpretation
would be possible, it could be technically unfeasible).

In a few cases having to write a complicated assignment operator simply
to be able to "reset" the object might be too much work to be worth the
effort.
 
F

Francesco S. Carta

Juha Nieminen said:
  In some cases assignment could be a very heavy operation (at least
unless your compiler already supports rvalue references).

  In other cases assignment might not have any meaningful interpretation
with the type in question, as it might be logically impossible to create
copies of an object of a certain type (or even if a logical interpretation
would be possible, it could be technically unfeasible).

  In a few cases having to write a complicated assignment operator simply
to be able to "reset" the object might be too much work to be worth the
effort.

But then, if you need to reset that object, you'd have to write a
reset function which wouldn't be much less complicated than the
assignment operator.

This is going a bit too much on the vague and speculative field :)

OP?

(I mistaken Leigh's statement about not wanting to write the
assignment operator, I've taken is as not wanting to allow access to
the client code, never mind)
 
J

Juha Nieminen

Francesco S. Carta said:
But then, if you need to reset that object, you'd have to write a
reset function which wouldn't be much less complicated than the
assignment operator.

Not really, as technically speaking it's enough to destroy the
existing object and construct a new one on its place (as the original
poster suggested). Since the constructor and destructor are mandatory,
they already exist, so the "resetting" functionality can be built by
calling them.

Of course it will be slightly "hackish". However, I don't see how it
would be non-standard and/or non-portable.
 
F

Francesco S. Carta

Juha Nieminen said:
  Not really, as technically speaking it's enough to destroy the
existing object and construct a new one on its place (as the original
poster suggested). Since the constructor and destructor are mandatory,
they already exist, so the "resetting" functionality can be built by
calling them.

Uh... I didn't consider the opportunity of "hiding" that hack in the
member function, that would make the reset function way simpler than
the eventual assignment operator, you're right.
  Of course it will be slightly "hackish". However, I don't see how it
would be non-standard and/or non-portable.

That borders the reach of my skills and experience, seems fine to me
but I couldn't prove it.

All of this will come handy if I'll ever face such an issue - except
in case somebody here proves that this hack is really non standard or
non portable - this is currently being discussed elsethread, if I'm
not mistaken.
 
F

Francesco S. Carta

Leigh Johnston said:
Like I said earlier the "hack" fails miserably if the placement new
constructor throws an exception: you can end up calling a destructor twice
making this "hack" the antithesis of RAII, avoid; if making this a generic
function (as the OP does) then at the very least put the placement new in a
try/catch block and call abort() if something is thrown.  Also you cannot
use this "hack" if the class contains const or reference members.

Luckily, all my classes are simple enough that I can afford creating
assignment operators for them - so far, so good :)

Shall I need to use this hack, I'll definitely make its failure well
defined and controlled, just as you suggested.
 
J

Jorgen Grahn

Let's say I have a struct as follows:

Code:
struct MyStruct{

int a;

double b;

std::string str;

};

And let's say I create a global object of it which has all of its
members (including the intrinsic-type members) default-initialised:

Code:
MyStruct g_obj = MyStruct();

Half-way through my program's execution, I want to "reset" this global
object. By "reset", I mean I want all of its member to go back to the
state that they were in when the "g_obj" object was initially created.
I achieve this with the following function:

Code:
template<class T>
void ResetObject(T &obj)
{
obj.~T();  /* Destruct */

::new(&obj) T();  /* Construct */
}

This works fine for a struct/class object. However, I want this
template function to work for any kind of object, be it an intrinsic
or an array.

Regarding arrays: they aren't objects, and having them as naked
globals is not a good idea. Just refuse to support them, and make your
life easier. You can always nest the arrays in a struct or something.

/Jorgen
 
B

Bart van Ingen Schenau

Leigh Johnston said:
I'm not able to see any use for such a convoluted way of doing
something that simple - therefore I don't understand the reason of
your: "[...] should be used rarely".
The simple template I've posted above...
template<typename T> void reset(T& t) {
   t = T();
}
...will work just fine for all the types which are allowed to be
created (without parameters) and assigned.
@OP: my template will not work for resetting arrays - of course - and
the suggestion here would be to avoid them. If I need to create
several structures of the same type and store them in some container I
use a standard container such as an std::vector or an std::list.

Your template can also easily be overloaded to provide support for
arrays:

template <typename T, size_t N>
void reset(T (&t)[N])
{
for( int i=0; i<N; i++)
{
reset(t);
}
}

Bart v Ingen Schenau
 
F

Francesco S. Carta

Bart van Ingen Schenau said:
Leigh Johnston said:
"Francesco S. Carta"<[email protected]> wrote in messagenews:01ba1917-a753-4f09-8363-7f0c61d01002@r27g2000yqb.googlegroups.com...
I'm not able to see any use for such a convoluted way of doing
something that simple - therefore I don't understand the reason of
your: "[...] should be used rarely".
The simple template I've posted above...
template<typename T> void reset(T& t) {
t = T();
}
...will work just fine for all the types which are allowed to be
created (without parameters) and assigned.
@OP: my template will not work for resetting arrays - of course - and
the suggestion here would be to avoid them. If I need to create
several structures of the same type and store them in some container I
use a standard container such as an std::vector or an std::list.

Your template can also easily be overloaded to provide support for
arrays:

template<typename T, size_t N>
void reset(T (&t)[N])
{
for( int i=0; i<N; i++)
{
reset(t);
}
}


Ah, very nice, thanks for pointing that out.

That note of mine was somehow good for the sake of steering people away
from arrays, but if someone really wants to use them I don't think we
could do very much about it ;-)

(disclaimer: the first line above is not sarcastic as the second line
could make it appear, I didn't consider overloading it to support arrays)
 
J

James Kanze

The only reason I can think of for not wanting to write an
assignment operator is if the compiler generated one is
sufficient in which case assignment to a default constructed
object is sufficient for resetting anyway.

I'm not sure I've understood what you're saying correctly,
because I'm pretty sure you know this, but there are lots of
reasons why a class might not want to support assignment: most
polymorphic classes shouldn't, and of course, singletons won't.

Polymorphism is, of course, the basic reason why none of the
proposed solutions work. (The OP's solution, of course, will
cause problems even in the absense of polymorphism.) The
problem is that template type resolution is on the static type,
and to correctly reset an object, you need to act on the dynamic
type.

With regards to the original poster's problem, the easiest way
to reset an object to its initial state is to use a new object,
rather than the old one. I'm afraid I don't see much use for
his function, except in special cases. (It is sometimes useful
as an optimization technique to reuse a standard container. But
what makes it useful is that you don't completely reset it; you
keep any memory it has already allocated.)
 
J

James Kanze

I was not thinking and/or expressing myself clearly, my reply
else-thread: "I agree, what you said is more or less what
I was fishing for: of course an assignment operator is not
always suitable for a given type and writing one just to
support "resetting" it can be both tedious and present
a confusing interface compared to just employing the OP's
hack, however instead of employing the hack the addition of
a reset() function is more appropriate IMO."

Exactly. If a class supports "reset", then it should have
a reset function. Inversely, if it doesn't have a reset
function, we should assume that it doesn't support reset.
Trying to hack it some other way just doesn't cut it. You can't
reliably make a class do something it doesn't support.
 
J

James Kanze

[...]
Regarding arrays: they aren't objects,

They are according to the standard.
and having them as naked globals is not a good idea.

It depends. If you need to use them in constructors of static
objects, then arrays may avoid order of initialization issues.
And the initialization syntax is a lot simpler. I still use
them a lot for this (and have explicit support for them in my
basic library).
 
K

Keith H Duggar

    [...]
This works fine for a struct/class object. However, I want
this template function to work for any kind of object, be it
an intrinsic or an array.
Regarding arrays: they aren't objects,
They are according to the standard.

Damn. I made a guess and declared it as the truth. Sorry.

"It ain't so much the things we don't know that get us into
trouble. It's the things we do know that just ain't so."
-- Artemus Ward

"Those who don't know history are destined to repeat it."
-- Edmund Burke

;-)

KHD
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top