Memento Pattern, snapshot of abstract object?

The code below runs correctly (feel free to compile and run it yourself to see what it does). But when I turn Object abstract by uncommenting the line
virtual void foo() = 0;
it obviously won't compile. Any workaround to this problem? I want to use a copy constructor rather than restoring one data member at a time because I'm assuming that Object will have many, many data members (and also want to avoid extra responsibility when new data members are added to Object). Of course, using a pointer to Object in Memento will defeat the purpose of taking a snapshot.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <iostream>
#include <string>
#include <sstream>
#include <vector>

const std::string NAME = "Object";

template <typename T>
std::string toString (const T& t) {
	std::stringstream ss;
	ss << t;
	return ss.str();
}

class Memento;

class Object {
  	private:
    	    int value;
    	    std::string name;
    	    double decimal;  // and suppose there are loads of other data members
  	public:
	    Object (int newValue): value (newValue), name (NAME + toString (value)), decimal ((float)value / 100) {}
	    void doubleValue() {value = 2 * value;  name = NAME + toString (value);  decimal = (float)value / 100;}
	    void increaseByOne() {value++;  name = NAME + toString (value);  decimal = (float)value / 100;}
	    int getValue() const {return value;}
	    std::string getName() const {return name;}
	    double getDecimal() const {return decimal;}
	    Memento* createMemento() const;
	    void reinstateMemento (Memento* mem);
//	    virtual void foo() = 0;
};

class Memento {
  	private:
 	    Object object;
  	public:
    	    Memento (const Object& obj):  object (obj) {}
    	    Object snapshot() const {return object;}  // want a snapshot of Object itself because of its many data members
};

Memento* Object::createMemento() const {
	return new Memento (*this);
}

void Object::reinstateMemento (Memento* mem) {
	*this = mem->snapshot();
}

class Command {
  	private:
	    typedef void (Object::*Action)();
	    Object* receiver;
	    Action action;
	    static std::vector<Command*> commandList;
	    static std::vector<Memento*> mementoList;
	    static int numCommands;
	    static int maxCommands;
  	public:
	    Command (Object *newReceiver, Action newAction): receiver (newReceiver), action (newAction) {}
	    virtual void execute() {
	    	if (mementoList.size() < numCommands + 1)
	    		mementoList.resize (numCommands + 1);
	        mementoList[numCommands] = receiver->createMemento();  // saves the last value
	    	if (commandList.size() < numCommands + 1)
	    		commandList.resize (numCommands + 1);
	        commandList[numCommands] = this;  // saves the last command
	        if (numCommands > maxCommands)
	          	maxCommands = numCommands;
	        numCommands++;
	        (receiver->*action)();
	    }
	    static void undo() {
	        if (numCommands == 0)
	        {
	            std::cout << "There is nothing to undo at this point." << std::endl;
	            return;
	        }
	        commandList[numCommands - 1]->receiver->reinstateMemento (mementoList[numCommands - 1]);
	        numCommands--;
	    }
	    void static redo() {
	        if (numCommands > maxCommands)
	        {
	            std::cout << "There is nothing to redo at this point." << std::endl;
	            return ;
	        }
	        Command* commandRedo = commandList[numCommands];
	        (commandRedo->receiver->*(commandRedo->action))();
	        numCommands++;
	    }
};

std::vector<Command*> Command::commandList;
std::vector<Memento*> Command::mementoList;
int Command::numCommands = 0;
int Command::maxCommands = 0;

int main()
{
	int i;
	std::cout << "Please enter an integer: ";
	std::cin >> i;
	Object *object = new Object(i);
	
	Command *commands[3];
	commands[1] = new Command(object, &Object::doubleValue);
	commands[2] = new Command(object, &Object::increaseByOne);
	
	std::cout << "0.Exit,  1.Double,  2.Increase by one,  3.Undo,  4.Redo: ";
	std::cin >> i;
	
	while (i != 0)
	{
		if (i == 3)
		  	Command::undo();
		else if (i == 4)
		  	Command::redo();
		else if (i > 0 && i <= 2)
		  	commands[i]->execute();
		else
		{
			std::cout << "Enter a proper choice: ";
			std::cin >> i;
			continue;
		}
		std::cout << "   " << object->getValue() << "  " << object->getName() << "  " << object->getDecimal() << std::endl;
		std::cout << "0.Exit,  1.Double,  2.Increase by one,  3.Undo,  4.Redo: ";
		std::cin >> i;
	}
}
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>

struct base
{
    virtual ~base() = default ;
    virtual void foo( int, double ) = 0 ;
    virtual std::string bar() const = 0 ;

    struct memento { virtual ~memento() = default ; };
    virtual std::unique_ptr<memento> encapsulated_state() const = 0 ;
    virtual void restore_state( const std::unique_ptr<memento>& m ) = 0 ;
};

struct A : base
{
    virtual void foo( int a, double b ) override { i += a ; d += b ; }
    virtual std::string bar() const override
    { return "A{ " + std::to_string(i) + ", " + std::to_string(d) + " }" ; }

    class A_memento : public base::memento
    {
        int i ;
        double d ;
        A_memento( const A& a ) : i(a.i), d(a.d) {}
        friend struct A ;
    };

    virtual std::unique_ptr<memento> encapsulated_state() const override
    { return std::unique_ptr<memento>( new A_memento(*this) ) ; }

    virtual void restore_state( const std::unique_ptr<memento>& m ) override
    {
        const A_memento* am = dynamic_cast< const A_memento* >( m.get() ) ;
        if( am == nullptr ) throw std::invalid_argument( "incorrect memento") ;
        i = am->i ;
        d = am->d ;
    }

    private:
        int i = 0 ;
        double d = 0 ;
};

int main()
{
    std::unique_ptr<base> p( new A ) ;
    p->foo( 23, 67.89 ) ;
    std::cout << p->bar() << '\n' ; // A{ 23, 67.890000 }

    const auto old_state = p->encapsulated_state() ;

    p->foo( 123, 267.89 ) ;
    std::cout << p->bar() << '\n' ; // A{ 146, 335.780000 }

    p->restore_state(old_state) ;
    std::cout << p->bar() << '\n' ; // A{ 23, 67.890000 }
}

http://coliru.stacked-crooked.com/a/7daa6a3110e847ec


*** EDIT ***
> I want to use a copy constructor rather than restoring one data member at a time

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>

struct base
{
    virtual ~base() = default ;
    virtual void foo( int, double ) = 0 ;
    virtual std::string bar() const = 0 ;

    struct memento { virtual ~memento() = default ; };
    virtual std::unique_ptr<memento> encapsulated_state() const = 0 ;
    virtual void restore_state( const std::unique_ptr<memento>& m ) = 0 ;
};

struct A : base
{
    virtual void foo( int a, double b ) override { i += a ; d += b ; }
    virtual std::string bar() const override
    { return "A{ " + std::to_string(i) + ", " + std::to_string(d) + " }" ; }

    private: class A_memento : public base::memento
    {
        std::unique_ptr<A> copy_of_a ;
        A_memento( const A& a ) : copy_of_a( new A(a) ) {}
        friend struct A ;
    }; 
    public:

    virtual std::unique_ptr<memento> encapsulated_state() const override
    { return std::unique_ptr<memento>( new A_memento(*this) ) ; }

    virtual void restore_state( const std::unique_ptr<memento>& m ) override
    {
        const A_memento* am = dynamic_cast< const A_memento* >( m.get() ) ;
        if( am == nullptr ) throw std::invalid_argument( "incorrect memento") ;
        *this = *am->copy_of_a ;
    }

    private:
        int i = 0 ;
        double d = 0 ;
};

int main()
{
    std::unique_ptr<base> p( new A ) ;
    p->foo( 23, 67.89 ) ;
    std::cout << p->bar() << '\n' ; // A{ 23, 67.890000 }

    const auto old_state = p->encapsulated_state() ;

    p->foo( 123, 267.89 ) ;
    std::cout << p->bar() << '\n' ; // A{ 146, 335.780000 }

    p->restore_state(old_state) ;
    std::cout << p->bar() << '\n' ; // A{ 23, 67.890000 }
}

http://coliru.stacked-crooked.com/a/ad13d740a48df3af
Last edited on
I'm not sure I follow. What is Object::foo() supposed to do? Also, what's the point of making Object an abstract class if you don't use it polymorphically and no one inherits from it?
@helios
Object::foo() does nothing in the above code, because the code has been simplified to illustrate the problem at hand. In reality, assume Object is abstract and is polymorphic to many derived classes.

As usual, thank you very much JLBorges for coming to the rescue. I will study your solution. Your solutions are getting deeper as my problems get deeper, which is good.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>

struct base
{
    virtual ~base() = default ;
    virtual void foo( int, double ) = 0 ;
    virtual std::string bar() const = 0 ;

    struct memento { virtual ~memento() = default ; };
    virtual std::unique_ptr<memento> encapsulated_state() const = 0 ;
    virtual void restore_state( const std::unique_ptr<memento>& m ) = 0 ;
    
    protected: template < typename T > class memento_copy_constructor_impl : public memento
    {
        std::unique_ptr<T> copy ;
        memento_copy_constructor_impl( const T& t ) : copy( new T(t) ) {}
        friend T ;
    };
};

struct A : base
{
    virtual void foo( int a, double b ) override { i += a ; d += b ; }
    virtual std::string bar() const override
    { return "A{ " + std::to_string(i) + ", " + std::to_string(d) + " }" ; }

    using memento_type = memento_copy_constructor_impl<A> ;
    
    virtual std::unique_ptr<memento> encapsulated_state() const override
    { return std::unique_ptr<memento>( new memento_type(*this) ) ; }

    virtual void restore_state( const std::unique_ptr<memento>& m ) override
    {
        const memento_type* am = dynamic_cast< const memento_type* >( m.get() ) ;
        if( am == nullptr ) throw std::invalid_argument( "incorrect memento" ) ;
        *this = *am->copy ;
    }

    private:
        int i = 0 ;
        double d = 0 ;
};

int main()
{
    std::unique_ptr<base> p( new A ) ;
    p->foo( 23, 67.89 ) ;
    std::cout << p->bar() << '\n' ; // A{ 23, 67.890000 }

    const auto old_state = p->encapsulated_state() ;

    p->foo( 123, 267.89 ) ;
    std::cout << p->bar() << '\n' ; // A{ 146, 335.780000 }

    p->restore_state(old_state) ;
    std::cout << p->bar() << '\n' ; // A{ 23, 67.890000 }
}

http://coliru.stacked-crooked.com/a/a1d955dd9e27e496

Note:
Could use CRTP (to automatically generate encapsulated_state() and restore_state())
The clone pattern is an alternative.
Last edited on
CRTP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <string>
#include <memory>
#include <stdexcept>

struct base
{
    virtual ~base() = default ;
    virtual void foo( int, double ) = 0 ;
    virtual std::string bar() const = 0 ;

    struct memento { virtual ~memento() = default ; };
    virtual std::unique_ptr<memento> encapsulated_state() const = 0 ;
    virtual void restore_state( const std::unique_ptr<memento>& m ) = 0 ;
};

template < typename DERIVED > struct memento_copy_constructor_impl : base
{
    protected: 
        class memento_type : public base::memento
        {
            std::unique_ptr<DERIVED> copy ;
            explicit memento_type( const DERIVED& d ) : copy( new DERIVED(d) ) {}
            friend memento_copy_constructor_impl<DERIVED> ;
        };
    
    public: virtual std::unique_ptr<memento> encapsulated_state() const override
    { return std::unique_ptr<memento>( new memento_type( dynamic_cast<const DERIVED&>(*this) ) ) ; }

    virtual void restore_state( const std::unique_ptr<memento>& m ) override
    {
        const memento_type* am = dynamic_cast< const memento_type* >( m.get() ) ;
        if( am == nullptr ) throw std::invalid_argument( "incorrect memento" ) ;
        dynamic_cast<DERIVED&>(*this) = *am->copy ;
    }
};

struct A : memento_copy_constructor_impl<A>
{
    virtual void foo( int a, double b ) override { i += a ; d += b ; }
    virtual std::string bar() const override
    { return "A{ " + std::to_string(i) + ", " + std::to_string(d) + " }" ; }

    private:
        int i = 0 ;
        double d = 0 ;
};

struct B : memento_copy_constructor_impl<B>
{
    virtual void foo( int a, double b ) override { str = std::to_string(a) + std::to_string(b) ; }
    virtual std::string bar() const override { return "B{ '" + str + "' }" ; }

    private: std::string str ;
};

int main()
{
    for( auto& p : { std::unique_ptr<base>( new A ), std::unique_ptr<base>( new B ) } )
    {
        p->foo( 23, 67.89 ) ;
        std::cout << "first: " << p->bar() << '\n' ; 
    
        const auto old_state = p->encapsulated_state() ;
    
        p->foo( 123, 267.89 ) ;
        std::cout << "second: " << p->bar() << '\n' ; 
    
        p->restore_state(old_state) ;
        std::cout << "restored: " << p->bar() << "\n----------------------------\n" ; 
    }
}

http://coliru.stacked-crooked.com/a/f66a18117b585198
I'm trying to compare your CRTP version with my CRTP version to see if yours is more generalized than mine. You pulled out memento_copy_constructor_impl from base, so I have a feeling it does something extra now. Here was my version:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#include <iostream>
#include <string>
#include <memory>
#include <sstream>
#include <stdexcept>

template <typename T>
std::string toString (const T& t) {
	std::stringstream ss;
	ss << t;
	return ss.str();
}

class LivingBeing {
	public:
	    struct Memento {
			virtual ~Memento() = default; 
		};
	    virtual std::unique_ptr<Memento> createMemento() const = 0;
	    virtual void reinstateMemento (const std::unique_ptr<Memento>& m) = 0;

	    virtual ~LivingBeing() = default;
	    virtual void change (int, double) = 0;
	    virtual std::string display() const = 0;
    protected: 
		template <typename T> class 
		DerivedMemento: public Memento {
	        public:
				std::unique_ptr<T> copy;
		        DerivedMemento (const T& t): copy (new T(t)) {}
		        friend T;
	    };
};

template<typename DERIVED> 
class LivingBeingCRTP: public LivingBeing {
	private:
    	virtual std::unique_ptr<LivingBeing::Memento> createMemento() const override {
			return std::unique_ptr<Memento> (new DerivedMemento<DERIVED> (dynamic_cast<const DERIVED&>(*this)));
		}  
		virtual void reinstateMemento (const std::unique_ptr<LivingBeing::Memento>& memento) override {
	        const DerivedMemento<DERIVED>* m = dynamic_cast<const DerivedMemento<DERIVED>*>(memento.get());
	        if (m == nullptr)
				throw std::invalid_argument ("Incorrect Memento");
//	        *this = *m->copy;  // my original line, which I had to debug
			dynamic_cast<DERIVED&>(*this) = *m->copy;
	    }
};

class Fighter: public LivingBeingCRTP<Fighter> {
    private:
        int i = 0;
        double d = 0;
    public:
	    virtual void change (int a, double b) override {i += a; d += b;}
	    virtual std::string display() const override {return "Fighter {" + toString(i) + ", " + toString(d) + "}";}
};

int main() {
    LivingBeing* livingBeing = new Fighter;
    livingBeing->change (20, 1.5);
    std::cout << livingBeing->display() << std::endl;  // Fighter {20, 1.5}
    const std::unique_ptr<LivingBeing::Memento> snapshot = livingBeing->createMemento();
    livingBeing->change (5, 0.4);
	std::cout << livingBeing->display() << std::endl;  // Fighter {25, 1.9}
    livingBeing->reinstateMemento (snapshot);
    std::cout << livingBeing->display() << std::endl;  // Fighter {20, 1.5}
} 


Then I created a second concrete derived class like you did and tested it and it worked. So I guess there is no difference in workability. As for flexibility, I'm not sure.

I will try your suggested clone method next.
Last edited on
Well here is my usage of the clone method with your CRTP version (my CRTP version encapsulates less nicely than yours I suppose):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <iostream>
#include <string>
#include <memory>
#include <sstream>
#include <stdexcept>

template <typename T>
std::string toString (const T& t) {
	std::stringstream ss;
	ss << t;
	return ss.str();
}

class LivingBeing {
	private:
	    struct Memento {
	        virtual ~Memento() = default;
	    };
	public:
	    virtual ~LivingBeing() = default;
	    virtual void change (int, double) = 0;
	    virtual std::string display() const = 0;
	    virtual std::unique_ptr<Memento> createMemento() const = 0;
	    virtual void restoreMemento (const std::unique_ptr<Memento>& m) = 0;
	protected:
	    virtual LivingBeing* clone() const = 0;
};

template <typename DERIVED> 
class LivingBeingCRTP: public LivingBeing {
    private: 
        class DerivedMemento: public LivingBeing::Memento {
        	std::unique_ptr<DERIVED> copy;
	        explicit DerivedMemento (const DERIVED& d) : copy (new DERIVED(d)) {}
	        friend class LivingBeingCRTP<DERIVED>;
        };
    public: 
		virtual std::unique_ptr<Memento> createMemento() const override {
			const DERIVED* derived = dynamic_cast<const DERIVED*>(clone());
			return std::unique_ptr<Memento> (new DerivedMemento (*derived));
		}
	    virtual void restoreMemento (const std::unique_ptr<Memento>& memento) override {
	        const DerivedMemento* m = dynamic_cast<const DerivedMemento*>(memento.get());
	        if (m == nullptr)
				throw std::invalid_argument ("Incorrect Memento");
	        dynamic_cast<DERIVED&>(*this) = *m->copy;
	    }
};

class Fighter: public LivingBeingCRTP<Fighter> {
    private:
        int i = 0;
        double d = 0;
    public:
	    virtual void change (int a, double b) override {i += a; d += b;}
	    virtual std::string display() const override {return "Fighter {" + toString(i) + ", " + toString(d) + "}" ;}
    private:
        virtual LivingBeing* clone() const override {return new Fighter(*this);}
};

class MagicUser: public LivingBeingCRTP<MagicUser> {
    private: 
        std::string name;
    public: 
	virtual void change (int a, double b) override {name = toString(a) + toString(b);}
    	virtual std::string display() const override {return "MagicUser {" + name + "}";}
    private:
    	virtual LivingBeing* clone() const override {return new MagicUser(*this);}
};

int main() {
    for (auto& p: {std::unique_ptr<LivingBeing>(new Fighter), std::unique_ptr<LivingBeing>(new MagicUser)})
    {
        p->change (23, 67.89) ;
        std::cout << "first: " << p->display() << std::endl; 
        const auto snapShot = p->createMemento();
        p->change (123, 267.89);
        std::cout << "second: " << p->display() << std::endl;
        p->restoreMemento (snapShot);
        std::cout << "restored: " << p->display() << "\n----------------------------\n";
    }
    std::cin.get();
}


But does clone() really create better performance here? Is
 
const DERIVED* derived = dynamic_cast<const DERIVED*>(clone());

really the most efficient cloning method?

Finally, I will go back to the original problem at hand, where we carry out arbitrary number of undo's and redo's on an abstract Object (this time with actual concrete subtypes) using a Command class.
Last edited on
With clone() and assign(), the base class can fully implement the default memento functionality (with a simpler memento as a moveable value type).

CRTP is an optional add-on; it can provide canned implementations of clone() and assign().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <string>
#include <memory>
#include <sstream>
#include <stdexcept>

template <typename T>
std::string toString (const T& t) {
    std::stringstream ss;
	ss << t;
	return ss.str();
}

class LivingBeing {
	public:
        class Memento { // memento has value semantics (MoveConstructible,MoveAssignable)
                std::unique_ptr<LivingBeing> copy;
                Memento( const LivingBeing* being ) : copy( being->clone() ) {}
                friend class LivingBeing ;
	    };

        virtual ~LivingBeing() noexcept = default;
        
	    virtual void change (int, double) = 0;
	    virtual std::string display() const = 0;
        
        // with ploymorphic clone and assign, the base class can implement these
	    virtual Memento createMemento() const { return Memento(this) ; } // return memento by value
	    virtual void restoreMemento ( const Memento& m ) { assign( *m.copy ) ; }
        
	protected:
	    virtual std::unique_ptr<LivingBeing> clone() const = 0;
        virtual LivingBeing& assign ( const LivingBeing& ) = 0 ; // may throw std::bad_cast
};

template <typename DERIVED> // DERIVED class is CopyAssignable
class LivingBeingCRTP: public LivingBeing { // sythesizes overrides for clone() and assign()
    protected:
        virtual std::unique_ptr<LivingBeing> clone() const override 
        {return std::unique_ptr<LivingBeing>( new DERIVED( static_cast<const DERIVED&>(*this) ) );}
        
        virtual LivingBeing& assign ( const LivingBeing& being ) override // may throw std::bad_cast
        { return static_cast<DERIVED&>(*this) = dynamic_cast< const DERIVED& >(being) ; }
};

class Fighter: public LivingBeingCRTP<Fighter> {
    int i = 0;
    double d = 0;
    public:
	    virtual void change (int a, double b) override {i += a; d += b;}
	    virtual std::string display() const override 
           {return "Fighter {" + toString(i) + ", " + toString(d) + "}" ;}
};

class MagicUser: public LivingBeingCRTP<MagicUser> {
    std::string name;
    public: 
    	virtual void change (int a, double b) override {name = toString(a) + toString(b);}
        virtual std::string display() const override {return "MagicUser {" + name + "}";}
};

int main() {
    for (auto& p: {std::unique_ptr<LivingBeing>(new Fighter), std::unique_ptr<LivingBeing>(new MagicUser)})
    {
        p->change (23, 67.89) ;
        std::cout << "first: " << p->display() << std::endl; 
        const auto snapShot = p->createMemento();
        p->change (123, 267.89);
        std::cout << "second: " << p->display() << std::endl;
        p->restoreMemento (snapShot);
        std::cout << "restored: " << p->display() << "\n----------------------------\n";
    }
}

http://coliru.stacked-crooked.com/a/a74bcf61adf5a096
Ok, if any of you want to implement successive undo's and redo's of operations on a collection of objects in your program, including iterating over abstract types like in this example, you can use this as a prototype within your program. Feel free to run it and check its accuracy. Much credit goes to JLBorges, of course.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <iostream>
#include <string>
#include <memory>
#include <sstream>
#include <vector>
#include <list>
#include <cstdlib>
#include <ctime>

template <typename T>
std::string toString (const T& t) {
    std::stringstream ss;
	ss << t;
	return ss.str();
}

class LivingBeing {
	public:
        class Memento {
        	private:
            	std::unique_ptr<LivingBeing> copy;
            public:
	            Memento(): copy (nullptr) {}
	            Memento (const LivingBeing* being): copy (being->clone()) {}  // calls up the move assignment operator
	            Memento (Memento&& other) noexcept: copy (std::move (other.copy)) {
	            	other.copy.reset();
	            }
	            Memento& operator = (Memento&& other) noexcept {
	            	if (this == &other)
	            		return *this;
	            	copy = std::move (other.copy);
	            	other.copy.reset();
	            	return *this;
	            }
				friend class LivingBeing;
	    };
        virtual ~LivingBeing() noexcept = default;
	    virtual void change() = 0;
	    virtual void display() const = 0;
	    virtual Memento createMemento() const {return Memento (this);}
	    virtual LivingBeing& restoreMemento (const Memento& memento) {return assign (*memento.copy);}
	private:
	    virtual std::unique_ptr<LivingBeing> clone() const = 0;
        virtual LivingBeing& assign (const LivingBeing&) = 0 ;
};

template <typename DERIVED>
class LivingBeingCRTP: public LivingBeing {
    protected:
        virtual std::unique_ptr<LivingBeing> clone() const override {
			return std::unique_ptr<LivingBeing>(new DERIVED (static_cast<const DERIVED&>(*this)));
		}
        virtual LivingBeing& assign (const LivingBeing& being) override {  // return LivingBeing& because may throw std::bad_cast
        	return static_cast<DERIVED&>(*this) = dynamic_cast<const DERIVED&>(being);
		}
};

class Fighter: public LivingBeingCRTP<Fighter> {
    private:
        int i = 0;
        double d = 0;
	public:
	    virtual void change() override {i++; d += (float)(std::rand() % 10) / 10;}
	    virtual void display() const override {std::cout << "Fighter {" + toString(i) + ", " + toString(d) + "}" << std::endl;}
};

class MagicUser: public LivingBeingCRTP<MagicUser> {
    private: 
		std::string name;
	public: 
	    virtual void change() override {name = toString (std::rand() % 10) + toString ((float)(std::rand() % 10) / 10);}
    	virtual void display() const override {std::cout << "MagicUser {" + name + "}" << std::endl;}
};

class Command {
  	private:
  		typedef void (LivingBeing::*Action)();
	    std::vector<LivingBeing*> receivers;
	    Action action;
	    static int numReceivers;
	    static std::vector<std::vector<Command*>> commandList;
	    static std::vector<std::vector<LivingBeing::Memento>> mementoList;
	    static std::size_t numCommands;
	    static std::size_t maxCommands;
	public:
	    Command (const std::vector<LivingBeing*>& newReceivers, Action newAction): receivers (newReceivers), action (newAction) {
                        numReceivers = receivers.size();
                    }
	    virtual void execute() {
	    	if (mementoList.size() < numCommands + 1)
	    	{
	    		mementoList.resize (numCommands + 1);
	    		mementoList[numCommands].resize (numReceivers);
	    	}
	        for (int i = 0; i < numReceivers; ++i)
				mementoList[numCommands][i] = receivers[i]->createMemento();
	    	if (commandList.size() < numCommands + 1)
	    	{
	    		commandList.resize (numCommands + 1);
	    		commandList[numCommands].resize (numReceivers);
	    	}
			for (int i = 0; i < numReceivers; ++i)
	        	commandList[numCommands][i] = this;
	        if (numCommands > maxCommands)
	          	maxCommands = numCommands;
	        numCommands++;
	        for (LivingBeing* x: receivers)
	        	(x->*action)();
	    	if (mementoList.size() < numCommands + 1)
	    	{
	    		mementoList.resize (numCommands + 1);
	    		mementoList[numCommands].resize (numReceivers);
	    	}
	    	if (commandList.size() < numCommands + 1)
	    	{
	    		commandList.resize (numCommands + 1);
	    		commandList[numCommands].resize (numReceivers);
	    	}
	    	for (int i = 0; i < numReceivers; ++i)
	    	{
	        	mementoList[numCommands][i] = receivers[i]->createMemento();
	        	commandList[numCommands][i] = this;
	        }
	    }
	    static void undo() {
	        if (numCommands == 0)
	        {
	            std::cout << std::endl << "[There is nothing to undo at this point.]" << std::endl << std::endl;
	            return;
	        }
	        for (int i = 0; i < numReceivers; ++i)
	        	commandList[numCommands - 1][i]->receivers[i]->restoreMemento (mementoList[numCommands - 1][i]);
	        numCommands--;
	    }
	    void static redo() {
	        if (numCommands > maxCommands)
	        {
	            std::cout << std::endl << "[There is nothing to redo at this point.]" << std::endl << std::endl;
	            return ;
	        }
			for (int i = 0; i < numReceivers; ++i)
	        	commandList[numCommands + 1][i]->receivers[i]->restoreMemento (mementoList[numCommands + 1][i]);
	        numCommands++;
	    }
};

int Command::numReceivers;
std::vector<std::vector<Command*>> Command::commandList;
std::vector<std::vector<LivingBeing::Memento>> Command::mementoList;
std::size_t Command::numCommands = 0;
std::size_t Command::maxCommands = 0;

int main() {
	srand (std::time (nullptr));
	int i;
	std::vector<LivingBeing*> players = {new Fighter, new MagicUser, new Fighter, new Fighter, new MagicUser};
	
	Command *commands[2];
	commands[1] = new Command (players, &LivingBeing::change);  // add whatever other types of commands from LivingBeing functions
	
	for (const LivingBeing* x: players)
		x->display();
	std::cout << std::endl << "0.Exit,  1.Change,  2.Undo,  3.Redo: ";
	std::cin >> i;
	
	while (i != 0)
	{
		if (i < 0 || i > 3)
		{
			std::cout << "Enter a proper choice: ";
			std::cin >> i;
			continue;
		}			
		else if (i == 2)
		  	Command::undo();
		else if (i == 3)
		  	Command::redo();
		else
		  	commands[i]->execute();
		for (const LivingBeing* x: players)
			x->display();
		std::cout << std::endl << "0.Exit,  1.Change,  2.Undo,  3.Redo: ";
		std::cin >> i;
	}
}
Last edited on
Topic archived. No new replies allowed.