00001 //---------------------------------------------------------------------------- 00002 /** @file GtpEngine.h 00003 Basic implementation of the Go Text Protocol (GTP). 00004 00005 Depends only on the standard C++ library for maximum reusability. 00006 If ponder or interrupt functionality is enabled by setting the macros 00007 GTPENGINE_PONDER and/or GTPENGINE_INTERRUPT to 1, the Boost.Thread library 00008 is also needed. 00009 00010 GtpEngine implements a GTP engine with some basic commands. This class 00011 is typically used as a base class for other GTP engines. 00012 GtpEngine::Register allows to register additional commands. 00013 GtpEngine::MainLoop starts the main command loop. 00014 Command handlers implement the interface GtpCallbackBase. 00015 For class member functions, such a callback can be constructed with 00016 the GtpCallback template class. 00017 Each callback function is passed a GtpCommand argument, which can be 00018 queried for arguments and used for writing the response to. 00019 GTP error responses are created by throwing an instance of GtpFailure. 00020 All such exceptions are caught in the main loop and converted 00021 into a response with error status. 00022 */ 00023 //---------------------------------------------------------------------------- 00024 00025 #ifndef GTPENGINE_H 00026 #define GTPENGINE_H 00027 00028 #include <cstddef> 00029 #include <iostream> 00030 #include <map> 00031 #include <sstream> 00032 #include <string> 00033 #include <vector> 00034 00035 #include "GtpInputStream.h" 00036 #include "GtpOutputStream.h" 00037 00038 #ifndef GTPENGINE_PONDER 00039 /** Macro for enabling pondering. 00040 If this macro is enabled, GtpEngine has the additional functions 00041 Ponder(), InitPonder() and StopPonder(), which will be called while 00042 waiting for the next command. This can be used for thinking during the 00043 opponent's time. 00044 Enabling this macro adds a dependency on the Boost.Thread library. 00045 @see GtpEngine::Ponder() 00046 */ 00047 #define GTPENGINE_PONDER 1 00048 #endif 00049 00050 #ifndef GTPENGINE_INTERRUPT 00051 /** Macro for enabling interrupt ability. 00052 If this macro is enabled, GtpEngine has the additional function 00053 Interrupt() to interrupt a running command. 00054 Enabling this macro adds a dependency on the Boost.Thread library. 00055 @see GtpEngine::Interrupt() 00056 */ 00057 #define GTPENGINE_INTERRUPT 1 00058 #endif 00059 00060 //---------------------------------------------------------------------------- 00061 00062 /** GTP failure. 00063 Command handlers generate a GTP error response by throwing an instance 00064 of GtpFailure. 00065 It contains an internal string stream for building the reponse using 00066 stream output operators. 00067 To make formatting of responses with a temporary object more convenient, 00068 operator<< uses non-standard semantics, such that a new object is 00069 returned. 00070 Usage examples: 00071 @verbatim 00072 // OK. Construct with string 00073 throw GtpFailure("message"); 00074 00075 // OK. Use temporary object 00076 throw GtpFailure() << message << ...; 00077 00078 // NOT OK. Object is not modified, the return value of << is ignored 00079 GtpFailure failure; 00080 failure << message << ...; 00081 throw failure; 00082 00083 // OK. Use the internal string stream 00084 GtpFailure failure; 00085 failure.ResponseStream() << message << ...; 00086 throw failure; 00087 @endverbatim 00088 */ 00089 class GtpFailure 00090 { 00091 public: 00092 /** Construct with no message. */ 00093 GtpFailure(); 00094 00095 /** Construct with message. */ 00096 GtpFailure(const std::string& response); 00097 00098 /** Copy constructor. 00099 Needed for operator<<. 00100 Preserves the internal string stream format state. 00101 */ 00102 GtpFailure(const GtpFailure& failure); 00103 00104 /** Destructor. */ 00105 ~GtpFailure() throw(); 00106 00107 /** Get the response. 00108 Returns a copy of the text in the internal string stream. 00109 */ 00110 std::string Response() const; 00111 00112 /** Get the internal string stream. */ 00113 std::ostream& ResponseStream(); 00114 00115 private: 00116 std::ostringstream m_response; 00117 }; 00118 00119 /** @relates GtpFailure 00120 @note Returns a new object, see @ref GtpFailure 00121 */ 00122 template<typename TYPE> 00123 GtpFailure operator<<(const GtpFailure& failure, const TYPE& type) 00124 { 00125 GtpFailure result(failure); 00126 result.ResponseStream() << type; 00127 return result; 00128 } 00129 00130 /** @relates GtpFailure 00131 @note Returns a new object, see @ref GtpFailure 00132 */ 00133 template<typename TYPE> 00134 GtpFailure operator<<(const GtpFailure& failure, TYPE& type) 00135 { 00136 GtpFailure result(failure); 00137 result.ResponseStream() << type; 00138 return result; 00139 } 00140 00141 inline std::string GtpFailure::Response() const 00142 { 00143 return m_response.str(); 00144 } 00145 00146 inline std::ostream& GtpFailure::ResponseStream() 00147 { 00148 return m_response; 00149 } 00150 00151 //---------------------------------------------------------------------------- 00152 00153 00154 00155 /** GTP command. 00156 GtpCommands are passed to command handlers. 00157 They can be queried for arguments and used for writing the response to. 00158 00159 Arguments can contain spaces if they are double quoted, for instance: 00160 @verbatim loadsgf "My File.sgf" @endverbatim 00161 Double quotes in a quoted argument have to be escaped with '\'. 00162 00163 The response message format does not have any special requirement, 00164 it will be sanitized by GtpEngine before writing to form a valid 00165 GTP response (see @ref GtpEngine::MainLoop). 00166 */ 00167 00168 class GtpCommand 00169 { 00170 public: 00171 /** Construct empty command. 00172 @warning An empty command cannot be used, before Init() was called. 00173 This constructor exists only to reuse instances. 00174 */ 00175 GtpCommand(); 00176 00177 /** Construct with a command line. 00178 @see Init() 00179 */ 00180 GtpCommand(const std::string& line); 00181 00182 /** Conversion to output stream. 00183 Returns reference to response stream. 00184 */ 00185 operator std::ostream&(); 00186 00187 /** Get argument. 00188 @param number Argument index starting with 0 00189 @return Argument value 00190 @throws GtpFailure If no such argument 00191 */ 00192 const std::string& Arg(std::size_t number) const; 00193 00194 /** Get single argument. 00195 @return Argument value 00196 @throws GtpFailure If no such argument or command has more than one 00197 arguments 00198 */ 00199 const std::string& Arg() const; 00200 00201 /** Get argument converted to lowercase. 00202 @param number Argument index starting with 0 00203 @return Copy of argument value converted to lowercase 00204 @throws GtpFailure If no such argument 00205 */ 00206 std::string ArgToLower(std::size_t number) const; 00207 00208 /** Get integer argument converted boolean. 00209 @param number Argument index starting with 0 00210 @return false, if argument is 0, true, if 1 00211 @throws GtpFailure If no such argument, or argument has other value 00212 */ 00213 bool BoolArg(std::size_t number) const; 00214 00215 /** Check that command has no arguments. 00216 @throws GtpFailure If command has arguments 00217 */ 00218 void CheckArgNone() const; 00219 00220 /** Check number of arguments. 00221 @param number Expected number of arguments 00222 @throws GtpFailure If command has a different number of arguments 00223 */ 00224 void CheckNuArg(std::size_t number) const; 00225 00226 /** Check maximum number of arguments. 00227 @param number Expected maximum number of arguments 00228 @throws GtpFailure If command has more arguments 00229 */ 00230 void CheckNuArgLessEqual(std::size_t number) const; 00231 00232 /** Get argument converted to double. 00233 @param number Argument index starting with 0 00234 @return Argument value 00235 @throws GtpFailure If no such argument, or argument is not a double 00236 */ 00237 double FloatArg(std::size_t number) const; 00238 00239 /** Get command ID. 00240 @return ID or empty string, if command has no ID 00241 */ 00242 std::string ID() const; 00243 00244 /** Initialize with a command line. 00245 The line should be not empty, not contain only whitespaces and not 00246 be a comment line. 00247 It will be split into the optional numeric command ID, the command 00248 name, and arguments. 00249 */ 00250 void Init(const std::string& line); 00251 00252 /** Get argument converted to integer. 00253 @param number Argument index starting with 0 00254 @return Argument value 00255 @throws GtpFailure If no such argument, or argument is not an integer 00256 */ 00257 int IntArg(std::size_t number) const; 00258 00259 /** Get argument converted to integer in a range with lower limit. 00260 @param number Argument index starting with 0 00261 @param min Minimum allowed value 00262 @return Argument value 00263 @throws GtpFailure If no such argument, argument is not an integer, 00264 or not in range 00265 */ 00266 int IntArg(std::size_t number, int min) const; 00267 00268 /** Get argument converted to integer in a range with lower and upper 00269 limit. 00270 @param number Argument index starting with 0 00271 @param min Minimum allowed value 00272 @param max Maximum allowed value 00273 @return Argument value 00274 @throws GtpFailure If no such argument, argument is not an integer, 00275 or not in range 00276 */ 00277 int IntArg(std::size_t number, int min, int max) const; 00278 00279 /** Get argument line. 00280 Get all arguments as a line. 00281 No modfications to the line were made apart from trimmimg leading 00282 and trailing white spaces. 00283 */ 00284 std::string ArgLine() const; 00285 00286 /** Get command line. 00287 Returns full command line as given to the constructor or 00288 GtpCommand::Init. 00289 No modfications to the line were made apart from trimmimg leading 00290 and trailing white spaces. 00291 */ 00292 const std::string& Line() const; 00293 00294 /** Get argument name. */ 00295 const std::string& Name() const; 00296 00297 /** Get number of arguments. */ 00298 std::size_t NuArg() const; 00299 00300 /** Return remaining line after argument. 00301 @param number Argument index starting with 0 00302 @return The remaining line after the given argument, unmodified apart 00303 from leading and trailing whitespaces, which are trimmed. Quotation 00304 marks are not handled. 00305 @throws GtpFailure If no such argument 00306 */ 00307 std::string RemainingLine(std::size_t number) const; 00308 00309 /** Get response. 00310 @return A copy of the internal response string stream 00311 */ 00312 std::string Response() const; 00313 00314 /** Get internal response string stream */ 00315 std::ostringstream& ResponseStream(); 00316 00317 /** Set response. */ 00318 void SetResponse(const std::string& response); 00319 00320 /** Set response to "true" or "false". */ 00321 void SetResponseBool(bool value); 00322 00323 /** Get argument converted to std::size_t. 00324 @param number Argument index starting with 0 00325 @return Argument value 00326 @throws GtpFailure If no such argument, or argument is not a size_t 00327 */ 00328 std::size_t SizeTypeArg(std::size_t number) const; 00329 00330 /** Get argument converted to std::size_t with lower limit. 00331 @param number Argument index starting with 0 00332 @param min Minimum allowed value 00333 @return Argument value 00334 @throws GtpFailure If no such argument, or argument is not a size_t 00335 */ 00336 std::size_t SizeTypeArg(std::size_t number, std::size_t min) const; 00337 00338 private: 00339 /** Argument in command line. */ 00340 struct Argument 00341 { 00342 /** Argument value. 00343 Enclosing quotes are removed if there were any and escape 00344 characters within enclosing quotes are removed. 00345 */ 00346 std::string m_value; 00347 00348 /** Position of first character in m_line after this argument. */ 00349 std::size_t m_end; 00350 00351 Argument(const std::string& value, std::size_t end); 00352 }; 00353 00354 /** Dummy stream for copying default formatting settings. */ 00355 static std::ostringstream s_dummy; 00356 00357 /** ID of command or empty string, if command has no ID */ 00358 std::string m_id; 00359 00360 /** Full command line. */ 00361 std::string m_line; 00362 00363 /** Response stream */ 00364 std::ostringstream m_response; 00365 00366 /** Arguments of command. */ 00367 std::vector<Argument> m_arguments; 00368 00369 void ParseCommandId(); 00370 00371 void SplitLine(const std::string& line); 00372 }; 00373 00374 /** @relates GtpCommand */ 00375 template<typename TYPE> 00376 GtpCommand& operator<<(GtpCommand& cmd, const TYPE& type) 00377 { 00378 cmd.ResponseStream() << type; 00379 return cmd; 00380 } 00381 00382 /** @relates GtpCommand */ 00383 template<typename TYPE> 00384 GtpCommand& operator<<(GtpCommand& cmd, TYPE& type) 00385 { 00386 cmd.ResponseStream() << type; 00387 return cmd; 00388 } 00389 00390 inline GtpCommand::GtpCommand() 00391 { 00392 } 00393 00394 inline GtpCommand::GtpCommand(const std::string& line) 00395 { 00396 Init(line); 00397 } 00398 00399 inline GtpCommand::operator std::ostream&() 00400 { 00401 return ResponseStream(); 00402 } 00403 00404 inline void GtpCommand::CheckArgNone() const 00405 { 00406 CheckNuArg(0); 00407 } 00408 00409 inline std::string GtpCommand::ID() const 00410 { 00411 return m_id; 00412 } 00413 00414 inline const std::string& GtpCommand::Line() const 00415 { 00416 return m_line; 00417 } 00418 00419 inline const std::string& GtpCommand::Name() const 00420 { 00421 return m_arguments[0].m_value; 00422 } 00423 00424 inline std::size_t GtpCommand::NuArg() const 00425 { 00426 return m_arguments.size() - 1; 00427 } 00428 00429 inline std::string GtpCommand::Response() const 00430 { 00431 return m_response.str(); 00432 } 00433 00434 inline std::ostringstream& GtpCommand::ResponseStream() 00435 { 00436 return m_response; 00437 } 00438 00439 //---------------------------------------------------------------------------- 00440 00441 /** Abstract base class for command handlers. */ 00442 class GtpCallbackBase 00443 { 00444 public: 00445 virtual ~GtpCallbackBase() throw(); 00446 00447 virtual void operator()(GtpCommand&) = 0; 00448 }; 00449 00450 //---------------------------------------------------------------------------- 00451 00452 /** Member function command handlers. 00453 For registering member functions in GtpEngine::Register(). 00454 @note Instances keep a pointer to the object containing the member 00455 function. If the object does is not a subclass of GtpEngine and registers 00456 only its own members, you have to make sure that the object's lifetime 00457 exceeds the lifetime of the GtpEngine. 00458 */ 00459 template<class ENGINE> 00460 class GtpCallback 00461 : public GtpCallbackBase 00462 { 00463 public: 00464 /** Signature of the member function. */ 00465 typedef void (ENGINE::*Method)(GtpCommand&); 00466 00467 GtpCallback(ENGINE* instance, 00468 typename GtpCallback<ENGINE>::Method method); 00469 00470 ~GtpCallback() throw(); 00471 00472 /** Execute the member function. */ 00473 void operator()(GtpCommand&); 00474 00475 private: 00476 ENGINE* m_instance; 00477 00478 Method m_method; 00479 }; 00480 00481 template<class ENGINE> 00482 GtpCallback<ENGINE>::GtpCallback(ENGINE* instance, 00483 typename GtpCallback<ENGINE>::Method method) 00484 : m_instance(instance), 00485 m_method(method) 00486 { 00487 } 00488 00489 template<class ENGINE> 00490 GtpCallback<ENGINE>::~GtpCallback() throw() 00491 { 00492 #ifndef NDEBUG 00493 m_instance = 0; 00494 #endif 00495 } 00496 00497 template<class ENGINE> 00498 void GtpCallback<ENGINE>::operator()(GtpCommand& cmd) 00499 { 00500 (m_instance->*m_method)(cmd); 00501 } 00502 00503 //---------------------------------------------------------------------------- 00504 00505 /** @page gtpenginesimulatedelay Simulated Delays 00506 If the engine receives a special comment line 00507 <code># gtpengine-sleep n</code>, it will sleep for @c n seconds before 00508 reading the next line. This feature can be used for adding simulated 00509 delays in test GTP scripts (e.g. to test pondering functionality). 00510 Note that the start time for sleeping is at the start of the previous 00511 command, not when the response to the previous command is received, 00512 because GtpEngine continues reading the stream while the previous command 00513 is in progress. This functionality is only enabled, if GtpEngine was 00514 compiled with GTPENGINE_INTERRUPT. 00515 */ 00516 00517 /** Base class for GTP (Go Text Protocol) engines. 00518 Commands can be added with GtpEngine::Register(). 00519 Existing commands can be overridden by registering a new handler for 00520 the command or by overriding the command handler member function 00521 in subclasses. 00522 @see @ref gtpenginecommands, @ref gtpenginesimulatedelay 00523 */ 00524 class GtpEngine 00525 { 00526 public: 00527 /** @page gtpenginecommands GtpEngine Commands 00528 - @link CmdKnownCommand() @c known_command @endlink 00529 - @link CmdListCommands() @c list_commands @endlink 00530 - @link CmdName() @c name @endlink 00531 - @link CmdProtocolVersion() @c protocol_version @endlink 00532 - @link CmdQuit() @c quit @endlink 00533 - @link CmdVersion() @c version @endlink 00534 */ 00535 /** @name Command Callbacks */ 00536 // @{ 00537 virtual void CmdKnownCommand(GtpCommand&); 00538 virtual void CmdListCommands(GtpCommand&); 00539 virtual void CmdName(GtpCommand&); 00540 virtual void CmdProtocolVersion(GtpCommand&); 00541 virtual void CmdQuit(GtpCommand&); 00542 virtual void CmdVersion(GtpCommand&); 00543 // @} // @name 00544 00545 /** Constructor. 00546 @param in Input GTP stream 00547 @param out Output GTP stream 00548 */ 00549 GtpEngine(GtpInputStream& in, GtpOutputStream& out); 00550 00551 virtual ~GtpEngine(); 00552 00553 /** Execute commands from file. 00554 Aborts on the first command that fails. 00555 @param name The file name 00556 @param log Stream for logging the commands and responses to (default 00557 is std::cerr). 00558 @throw GtpFailure If a command fails 00559 */ 00560 void ExecuteFile(const std::string& name, std::ostream& log = std::cerr); 00561 00562 /** Execute a single command string. 00563 @param cmd The command line 00564 @param log Stream for logging the command and response to (default 00565 is std::cerr). 00566 @returns The command response 00567 @throw GtpFailure If the command fails 00568 */ 00569 std::string ExecuteCommand(const std::string& cmd, 00570 std::ostream& log = std::cerr); 00571 00572 /** Run the main command loop. 00573 Reads lines from input stream, calls the corresponding command 00574 handler and writes the response to the output stream. 00575 Empty lines in the command responses will be replaced by a line 00576 containing a single space, because empty lines are not allowed 00577 in GTP responses. 00578 */ 00579 void MainLoop(); 00580 00581 /** Register command handler. 00582 Takes ownership of callback. 00583 If a command was already registered with the same name, 00584 it will be replaced by the new command. 00585 */ 00586 void Register(const std::string& name, GtpCallbackBase* callback); 00587 00588 /** Register a member function as a command handler. 00589 If a command was already registered with the same name, 00590 it will be replaced by the new command. 00591 */ 00592 template<class T> 00593 void Register(const std::string& command, 00594 typename GtpCallback<T>::Method method, T* instance); 00595 00596 /** Returns if command registered. */ 00597 bool IsRegistered(const std::string& command) const; 00598 00599 /** Set flag for quitting the main command loop. 00600 Currently, this function works only for the "quit" command, if the 00601 engine is compiled with interrupt functionality (GTENGINE_INTERRUPT). 00602 Therefore, it is not possible for other commands to decide to quit 00603 (which would be necessary for instance to implement a maximal game 00604 number if playing on KGS and deciding to quit on the kgs-game_over 00605 command, if the maximum number is reached). 00606 00607 The reason is that the command stream is then read from a different 00608 thread using a blocking, non-interruptible read function, which is 00609 entered before the command handler is invoked in the main thread. 00610 Because of the non-interruptible read function, the implementation of 00611 GtpEngine needs to know what commands will quit to avoid entering this 00612 read function after a quit. 00613 00614 If a way is found to interrupt the read thread during the execution 00615 of the blocking std::getline (maybe in a future version of 00616 Boost.Thread), this function could also be called in other GTP 00617 commands. 00618 @see MainLoop() 00619 */ 00620 void SetQuit(); 00621 00622 /** Did the last command set the quit flag? */ 00623 bool IsQuitSet() const; 00624 00625 #if GTPENGINE_PONDER 00626 /** Ponder. 00627 This function will be called in MainLoop() while the engine is waiting 00628 for the next command. 00629 It will be called after InitPonder() from a different thread than 00630 the command thread, but only while waiting for the next command, so 00631 no concurrent execution of this function and other engine functions 00632 is possible. The function should return immediately when StopPonder() 00633 is called. InitPonder() and StopPonder() are called from the 00634 command thread. 00635 In a typical implementation, InitPonder() will clear an abort flag and 00636 StopPonder() will set it. Ponder() will poll the abort flag and return 00637 when it is set (or it has nothing to do; or some maximum time limit 00638 for pondering was exceeded). 00639 The default implementation does nothing and returns immediately. 00640 */ 00641 virtual void Ponder(); 00642 00643 /** Prepare for pondering. 00644 @see Ponder() 00645 The default implementation does nothing. 00646 */ 00647 virtual void InitPonder(); 00648 00649 /** Stop pondering. 00650 @see Ponder() 00651 The default implementation does nothing. 00652 */ 00653 virtual void StopPonder(); 00654 #endif // GTPENGINE_PONDER 00655 00656 #if GTPENGINE_INTERRUPT 00657 /** Interrupt the current command. 00658 This function implements interrupt functionality as used by 00659 <a href="http://gogui.sf.net">GoGui</a>. It will be called from a 00660 different thread that the command thread when the special command 00661 line <tt># interrupt</tt> is received. 00662 The default implementation does nothing. 00663 */ 00664 virtual void Interrupt(); 00665 #endif // GTPENGINE_INTERRUPT 00666 00667 protected: 00668 /** Hook function to be executed before each command. 00669 Default implementation does nothing. 00670 */ 00671 virtual void BeforeHandleCommand(); 00672 00673 /** Hook function to be executed before the response of a command is 00674 written. 00675 Default implementation does nothing. 00676 */ 00677 virtual void BeforeWritingResponse(); 00678 00679 private: 00680 typedef std::map<std::string,GtpCallbackBase*> CallbackMap; 00681 00682 bool m_quit; 00683 00684 GtpInputStream& m_in; 00685 00686 GtpOutputStream& m_out; 00687 00688 CallbackMap m_callbacks; 00689 00690 /** Not to be implemented. */ 00691 GtpEngine(const GtpEngine& engine); 00692 00693 /** Not to be implemented. */ 00694 GtpEngine& operator=(const GtpEngine& engine) const; 00695 00696 bool HandleCommand(GtpCommand& cmd, GtpOutputStream& out); 00697 }; 00698 00699 template<class T> 00700 void GtpEngine::Register(const std::string& command, 00701 typename GtpCallback<T>::Method method, T* instance) 00702 { 00703 Register(command, new GtpCallback<T>(instance, method)); 00704 } 00705 00706 //---------------------------------------------------------------------------- 00707 00708 #endif // GTPENGINE_H 00709