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