On Tuesday, 10 October 2017 at 02:36:56 UTC, Mr. Jonse wrote:
I requiring an undo feature in my code. Rather than go the
regular route of using commands, I'm wondering if D can
facilitate an undo system quite easily?
We can think of an undo system in an app as a sort of recorder.
The traditional method is to use commands and inverse-commands.
By recording the commands one can "unwind" the program by
applying the inverse commands. The down side is that the app
must be written with this approach in mind.
Storing the complete state of the app is another way which some
apps use but usually it is too much data to store.
Since the only thing that one has to store from state of the
app to the next is the change in data long with creation and
deletion of the data, I think one could simplify the task?
Is it possible to write a generic system that records the the
entire state changes without much boilerplate?
I'm thinking that two types of attributes would work, one for
aggregates for creation and deletion of objects and one for
properties to handle the data changes. If D can be used to
automatically hook properties to have them report the changes
to the undo system and one can properly deal with object
creation and assignment, it might be a pretty sleek way to
support undo.
Help appreciated!
I wrote an undo system for a level editor once:
https://github.com/nicolasjinchereau/pizza-quest/blob/90d1a2ae75c1f80ee13cedcfb634c6de0f9528db/source/editor/History.h
That class made it trivial to implement unlimited undo/redo.
Each object that's passed to History::AddObjectState() has to
have `Undoable` implemented so that its state can be copied and
replaced later if the object needs to be restored. In D though,
you don't even really have to implement `Undoable`. You can make
something like AddObjectState() into a template that uses D's
`__traits`, or `tupleof` to record all of an object's fields into
some generic undo state. I wrote that code so long ago that I
don't really remember how I dealt with pointer ownership, but if
you use GC allocation, or POD types, it should be easy.
With an approach like this, you don't need a discrete set of
commands, but only objects that can be serialized before an
operation, and restored afterward if you don't like the result.