Line data Source code
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 0 : struct Command
31 : {
32 0 : 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
|