1 /* Copyright (C) 2009 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef INCLUDED_COMMANDPROC
19 #define INCLUDED_COMMANDPROC
20 
21 #include <string>
22 #include <list>
23 #include <map>
24 
25 #include "SharedMemory.h"
26 
27 namespace AtlasMessage
28 {
29 
30 struct Command
31 {
~CommandCommand32 	virtual ~Command() {}
33 	virtual void Do() = 0;
34 	virtual void Undo() = 0;
35 	virtual void Redo() = 0;
36 	virtual void Merge(Command* prev) = 0;
37 	virtual const char* GetType() const = 0;
38 };
39 
40 class CommandProc
41 {
42 public:
43 	CommandProc();
44 	~CommandProc();
45 
46 	// Should be called before shutting down, so it can free
47 	// references to entities/etc that are stored in commands
48 	void Destroy();
49 
50 	void Submit(Command* cmd);
51 
52 	void Undo();
53 	void Redo();
54 	void Merge();
55 
56 private:
57 	std::list<Command*> m_Commands;
58 	typedef std::list<Command*>::iterator cmdIt;
59 	// The 'current' command is the latest one which has been executed
60 	// (ignoring any that have been undone)
61 	cmdIt m_CurrentCommand;
62 };
63 
64 typedef Command* (*cmdHandler)(const void*);
65 typedef std::map<std::string, cmdHandler> cmdHandlers;
66 extern cmdHandlers& GetCmdHandlers();
67 
68 CommandProc& GetCommandProc();
69 
70 /*
71 We want the command handler implementations to be as pretty as possible - so let people write
72 
73 	BEGIN_COMMAND(CommandName)
74 	{
75 		int member;
76 		cCommandName() { ... }  // } both are optional; neither may
77 		~cCommandName() { ... } // }   make use of this->msg
78 
79 		void Do() { ... interact with this->msg ... }
80 		void Undo() { ... }
81 		void Redo() { ... }
82 
83 		void MergeIntoPrevious(cCommandName* prev) { ... } // iff this command is a mergeable one
84 	};
85 	END_COMMAND(CommandName)
86 
87 which looks almost exactly like a class definition, and is about the simplest
88 system I can find.
89 
90 
91 The following macros convert that into:
92 
93 	class cCommandName_base : public Command
94 	{
95 	protected:
96 		// Storage for data passed into this command
97 		dCommandName* msg;
98 	public:
99 		// Ensure msg is initialised to something 'safe'
100 		cCommandName_base : msg(NULL) {}
101 
102 		// MergeIntoPrevious should be overridden by mergeable commands, and implemented
103 		// to update 'prev' to include the effects of 'this'. (A subclass overriding
104 		// any function named 'MergeIntoPrevious' will hide this function, even if
105 		// the types differ.)
106 		void MergeIntoPrevious(void*) { ...error - needs to be overridden... }
107 	};
108 
109 	// Use 'struct' for automatic publicness - we want users to write as little as possible
110 	struct cCommandName : public cCommandName_base
111 	{
112 		// (user's command code - mostly overriding virtual methods from Command)
113 	}
114 
115 	// Subclass the command to add things which require knowledge of the
116 	// complete class definition
117 	class cCommandName_sub : public cCommandName
118 	{
119 	public:
120 		cCommandName_sub(dCommandName *data) { ...set msg... }
121 		~cCommandName_sub() { ...clear msg... }
122 
123 		// Implement the relevant virtual methods from the Command base class,
124 		// with automatic casting to the correct types and stuff
125 		virtual void Merge(Command* prev) { ...call MergeIntoPrevious... }
126 		virtual const char* GetType() const { return "CommandName"; }
127 
128 		// Factory method for creating an instance of this class, casting the
129 		// data pointer into the right form (to avoid forcing the generic
130 		// command-handling support code to worry about the types)
131 		static Command* Create(const void* data) { ...return new cCommandName_sub... }
132 	};
133 
134 	// Register.cpp wants to get that Create method, but it doesn't want to
135 	// load all the class definitions; so define a simple method that just
136 	// returns the address of Create
137 	cmdHandler cCommandName_create()
138 	{
139 		return &cCommandName_sub::Create;
140 	}
141 
142 // (TODO: make sure this stays in sync with the code below)
143 
144 */
145 
146 #define BEGIN_COMMAND(t) \
147 	class c##t##_base : public Command \
148 	{ \
149 	protected: \
150 		d##t* msg; \
151 	public: \
152 		c##t##_base() : msg(NULL) {} \
153 		void MergeIntoPrevious(void*) { debug_warn(L"MergeIntoPrevious unimplemented in command " WIDEN(#t)); } \
154 	}; \
155 	struct c##t : public c##t##_base
156 
157 #define END_COMMAND(t) \
158 	class c##t##_sub : public c##t \
159 	{ \
160 	public: \
161 		c##t##_sub(d##t* data) \
162 		{ \
163 			msg = data; \
164 		} \
165 		~c##t##_sub() \
166 		{ \
167 			/* (msg was allocated in mDoCommand(), using SHAREABLE_NEW) */ \
168 			AtlasMessage::ShareableDelete(msg); \
169 			msg = NULL; \
170 		} \
171 		virtual void Merge(Command* prev) { MergeIntoPrevious(static_cast<c##t##_sub*>(prev)); } \
172 		virtual const char* GetType() const { return #t; } \
173 		static Command* Create(const void* data) \
174 		{ \
175 			return new c##t##_sub (reinterpret_cast<d##t*>(const_cast<void*>(data))); \
176 		} \
177 	}; \
178 	cmdHandler c##t##_create() \
179 	{ \
180 		return &c##t##_sub ::Create; \
181 	}
182 
183 
184 }
185 
186 #endif // INCLUDED_COMMANDPROC
187