00001 //---------------------------------------------------------------------------- 00002 /** @file GtpEngine.cpp 00003 See GtpEngine.h */ 00004 //---------------------------------------------------------------------------- 00005 00006 #include "GtpEngine.h" 00007 00008 #include <iomanip> 00009 #include <cassert> 00010 #include <cctype> 00011 #include <fstream> 00012 00013 #if GTPENGINE_PONDER || GTPENGINE_INTERRUPT 00014 #include <boost/thread/barrier.hpp> 00015 #include <boost/thread/condition.hpp> 00016 #include <boost/thread/mutex.hpp> 00017 #include <boost/thread/thread.hpp> 00018 #include <boost/thread/xtime.hpp> 00019 00020 using boost::barrier; 00021 using boost::condition; 00022 using boost::mutex; 00023 using boost::thread; 00024 using boost::xtime; 00025 using boost::xtime_get; 00026 #endif 00027 00028 using namespace std; 00029 00030 #ifdef WIN32 00031 // Don't report Visual C++ warning 4355 ('this' : used in base member 00032 // initializer list) in default warning level 3. The constructors of 00033 // ReadThread and PonderThread store a reference to 'this', which is 00034 // allowed as long as 'this' is not used yet. 00035 #pragma warning(4:4355) 00036 #endif 00037 00038 //---------------------------------------------------------------------------- 00039 00040 /** Utility functions. */ 00041 namespace { 00042 00043 void Trim(string& str); 00044 00045 /** Check, if line contains a command. 00046 @param line The line to check. 00047 @return True, if command does not contain only whitespaces and is not a 00048 comment line. */ 00049 bool IsCommandLine(const string& line) 00050 { 00051 string trimmedLine = line; 00052 Trim(trimmedLine); 00053 return (! trimmedLine.empty() && trimmedLine[0] != '#'); 00054 } 00055 00056 #if ! GTPENGINE_INTERRUPT 00057 00058 /** Read next command from stream. 00059 @param in The input stream. 00060 @param[out] cmd The command (reused for efficiency) 00061 @return @c false on end-of-stream or read error. */ 00062 bool ReadCommand(GtpCommand& cmd, GtpInputStream& in) 00063 { 00064 string line; 00065 while (in.GetLine(line) && ! IsCommandLine(line)) 00066 { 00067 } 00068 if (in.EndOfInput()) 00069 return false; 00070 Trim(line); 00071 cmd.Init(line); 00072 return true; 00073 } 00074 #endif 00075 00076 /** Replace empty lines in a multi-line string by lines containing a single 00077 space. 00078 @param text The input string. 00079 @return The input string with all occurrences of "\n\n" replaced by 00080 "\n \n". */ 00081 string ReplaceEmptyLines(const string& text) 00082 { 00083 if (text.find("\n\n") == string::npos) 00084 return text; 00085 istringstream in(text); 00086 ostringstream result; 00087 bool lastWasNewLine = false; 00088 char c; 00089 while (in.get(c)) 00090 { 00091 bool isNewLine = (c == '\n'); 00092 if (isNewLine && lastWasNewLine) 00093 result.put(' '); 00094 result.put(c); 00095 lastWasNewLine = isNewLine; 00096 } 00097 return result.str(); 00098 } 00099 00100 /** Remove leading and trailing whitespaces from a string. 00101 Whitespaces are tab, carriage return and space. 00102 @param str The input string. */ 00103 void Trim(string& str) 00104 { 00105 char const* whiteSpace = " \t\r"; 00106 size_t pos = str.find_first_not_of(whiteSpace); 00107 str.erase(0, pos); 00108 pos = str.find_last_not_of(whiteSpace); 00109 str.erase(pos + 1); 00110 } 00111 00112 } // namespace 00113 00114 //---------------------------------------------------------------------------- 00115 00116 #if GTPENGINE_PONDER || GTPENGINE_INTERRUPT 00117 00118 /** Utility functions for Boost.Thread. */ 00119 namespace { 00120 00121 void Notify(mutex& aMutex, condition& aCondition) 00122 { 00123 mutex::scoped_lock lock(aMutex); 00124 aCondition.notify_all(); 00125 } 00126 00127 } // namespace 00128 00129 #endif // GTPENGINE_PONDER || GTPENGINE_INTERRUPT 00130 00131 //---------------------------------------------------------------------------- 00132 00133 #if GTPENGINE_PONDER 00134 00135 namespace { 00136 00137 /** Ponder thread used by GtpEngine::MainLoop(). 00138 This thread calls GtpEngine::Ponder() while the engine is waiting for the 00139 next command. 00140 @see GtpEngine::Ponder() */ 00141 class PonderThread 00142 { 00143 public: 00144 PonderThread(GtpEngine& engine); 00145 00146 void StartPonder(); 00147 00148 void StopPonder(); 00149 00150 void Quit(); 00151 00152 private: 00153 class Function 00154 { 00155 public: 00156 Function(PonderThread& ponderThread); 00157 00158 void operator()(); 00159 00160 private: 00161 PonderThread& m_ponderThread; 00162 }; 00163 00164 friend class PonderThread::Function; 00165 00166 GtpEngine& m_engine; 00167 00168 barrier m_threadReady; 00169 00170 mutex m_startPonderMutex; 00171 00172 mutex m_ponderFinishedMutex; 00173 00174 condition m_startPonder; 00175 00176 condition m_ponderFinished; 00177 00178 mutex::scoped_lock m_ponderFinishedLock; 00179 00180 /** The thread to run the ponder function. 00181 Order dependency: must be constructed as the last member, because the 00182 constructor starts the thread. */ 00183 boost::thread m_thread; 00184 }; 00185 00186 PonderThread::Function::Function(PonderThread& ponderThread) 00187 : m_ponderThread(ponderThread) 00188 { 00189 } 00190 00191 void PonderThread::Function::operator()() 00192 { 00193 mutex::scoped_lock lock(m_ponderThread.m_startPonderMutex); 00194 m_ponderThread.m_threadReady.wait(); 00195 while (true) 00196 { 00197 m_ponderThread.m_startPonder.wait(lock); 00198 GtpEngine& engine = m_ponderThread.m_engine; 00199 if (engine.IsQuitSet()) 00200 return; 00201 engine.Ponder(); 00202 Notify(m_ponderThread.m_ponderFinishedMutex, 00203 m_ponderThread.m_ponderFinished); 00204 } 00205 } 00206 00207 PonderThread::PonderThread(GtpEngine& engine) 00208 : m_engine(engine), 00209 m_threadReady(2), 00210 m_ponderFinishedLock(m_ponderFinishedMutex), 00211 m_thread(Function(*this)) 00212 { 00213 m_threadReady.wait(); 00214 } 00215 00216 void PonderThread::StartPonder() 00217 { 00218 m_engine.InitPonder(); 00219 Notify(m_startPonderMutex, m_startPonder); 00220 } 00221 00222 void PonderThread::StopPonder() 00223 { 00224 m_engine.StopPonder(); 00225 m_ponderFinished.wait(m_ponderFinishedLock); 00226 } 00227 00228 void PonderThread::Quit() 00229 { 00230 Notify(m_startPonderMutex, m_startPonder); 00231 m_thread.join(); 00232 } 00233 00234 } // namespace 00235 00236 #endif // GTPENGINE_PONDER 00237 00238 //---------------------------------------------------------------------------- 00239 00240 #if GTPENGINE_INTERRUPT 00241 00242 namespace { 00243 00244 /** Thread for reading the next command line. 00245 This thread is used instead of the simple function 00246 ReadCommand(GtpCommand&), if GtpEngine is compiled with interrupt 00247 support. 00248 @see GtpEngine::Interrupt() */ 00249 class ReadThread 00250 { 00251 public: 00252 ReadThread(GtpInputStream& in, GtpEngine& engine); 00253 00254 bool ReadCommand(GtpCommand& cmd); 00255 00256 void JoinThread(); 00257 00258 private: 00259 class Function 00260 { 00261 public: 00262 Function(ReadThread& readThread); 00263 00264 void operator()(); 00265 00266 private: 00267 ReadThread& m_readThread; 00268 00269 void ExecuteSleepLine(const string& line); 00270 }; 00271 00272 friend class ReadThread::Function; 00273 00274 GtpInputStream& m_in; 00275 00276 GtpEngine& m_engine; 00277 00278 string m_line; 00279 00280 bool m_isStreamGood; 00281 00282 barrier m_threadReady; 00283 00284 mutex m_waitCommandMutex; 00285 00286 condition m_waitCommand; 00287 00288 mutex m_commandReceivedMutex; 00289 00290 condition m_commandReceived; 00291 00292 mutex::scoped_lock m_commandReceivedLock; 00293 00294 /** The thread to run the read command function. 00295 Order dependency: must be constructed as the last member, because the 00296 constructor starts the thread. */ 00297 boost::thread m_thread; 00298 }; 00299 00300 ReadThread::Function::Function(ReadThread& readThread) 00301 : m_readThread(readThread) 00302 { 00303 } 00304 00305 void ReadThread::Function::operator()() 00306 { 00307 mutex::scoped_lock lock(m_readThread.m_waitCommandMutex); 00308 m_readThread.m_threadReady.wait(); 00309 GtpEngine& engine = m_readThread.m_engine; 00310 GtpInputStream& in = m_readThread.m_in; 00311 string line; 00312 while (true) 00313 { 00314 while (in.GetLine(line)) 00315 { 00316 Trim(line); 00317 if (line == "# interrupt") 00318 engine.Interrupt(); 00319 else if (line.find("# gtpengine-sleep ") == 0) 00320 ExecuteSleepLine(line); 00321 else if (IsCommandLine(line)) 00322 break; 00323 } 00324 m_readThread.m_waitCommand.wait(lock); 00325 m_readThread.m_isStreamGood = ! in.EndOfInput(); 00326 m_readThread.m_line = line; 00327 Notify(m_readThread.m_commandReceivedMutex, 00328 m_readThread.m_commandReceived); 00329 if (in.EndOfInput()) 00330 return; 00331 // See comment at GtpEngine::SetQuit 00332 GtpCommand cmd(line); 00333 if (cmd.Name() == "quit") 00334 return; 00335 } 00336 } 00337 00338 void ReadThread::Function::ExecuteSleepLine(const string& line) 00339 { 00340 istringstream buffer(line); 00341 string s; 00342 buffer >> s; 00343 assert(s == "#"); 00344 buffer >> s; 00345 assert(s == "gtpengine-sleep"); 00346 int seconds; 00347 buffer >> seconds; 00348 if (seconds > 0) 00349 { 00350 cerr << "GtpEngine: sleep " << seconds << '\n'; 00351 xtime time; 00352 xtime_get(&time, boost::TIME_UTC); 00353 time.sec += seconds; 00354 thread::sleep(time); 00355 cerr << "GtpEngine: sleep done\n"; 00356 } 00357 } 00358 00359 void ReadThread::JoinThread() 00360 { 00361 m_thread.join(); 00362 } 00363 00364 ReadThread::ReadThread(GtpInputStream& in, GtpEngine& engine) 00365 : m_in(in), 00366 m_engine(engine), 00367 m_threadReady(2), 00368 m_commandReceivedLock(m_commandReceivedMutex), 00369 m_thread(Function(*this)) 00370 { 00371 m_threadReady.wait(); 00372 } 00373 00374 bool ReadThread::ReadCommand(GtpCommand& cmd) 00375 { 00376 Notify(m_waitCommandMutex, m_waitCommand); 00377 m_commandReceived.wait(m_commandReceivedLock); 00378 if (! m_isStreamGood) 00379 return false; 00380 cmd.Init(m_line); 00381 return true; 00382 } 00383 00384 } // namespace 00385 00386 #endif // GTPENGINE_INTERRUPT 00387 00388 //---------------------------------------------------------------------------- 00389 00390 GtpFailure::GtpFailure() 00391 { 00392 } 00393 00394 GtpFailure::GtpFailure(const GtpFailure& failure) 00395 { 00396 m_response << failure.Response(); 00397 m_response.copyfmt(failure.m_response); 00398 } 00399 00400 GtpFailure::GtpFailure(const string& response) 00401 { 00402 m_response << response; 00403 } 00404 00405 GtpFailure::~GtpFailure() throw() 00406 { 00407 } 00408 00409 //---------------------------------------------------------------------------- 00410 00411 GtpCommand::Argument::Argument(const string& value, std::size_t end) 00412 : m_value(value), 00413 m_end(end) 00414 { 00415 } 00416 00417 //---------------------------------------------------------------------------- 00418 00419 ostringstream GtpCommand::s_dummy; 00420 00421 const string& GtpCommand::Arg(std::size_t number) const 00422 { 00423 size_t index = number + 1; 00424 if (number >= NuArg()) 00425 throw GtpFailure() << "missing argument " << index; 00426 return m_arguments[index].m_value; 00427 } 00428 00429 const string& GtpCommand::Arg() const 00430 { 00431 CheckNuArg(1); 00432 return Arg(0); 00433 } 00434 00435 template<> 00436 std::size_t GtpCommand::Arg<std::size_t>(std::size_t i) const 00437 { 00438 // See the function declaration in GtpEngine.h for the rationale why this 00439 // template specialization is necessary. 00440 string arg = Arg(i); 00441 bool fail = (! arg.empty() && arg[0] == '-'); 00442 size_t result; 00443 if (! fail) 00444 { 00445 istringstream in(arg); 00446 in >> result; 00447 fail = ! in; 00448 } 00449 if (fail) 00450 throw GtpFailure() << "argument " << (i + 1) << " (" << arg 00451 << ") must be of type size_t"; 00452 return result; 00453 } 00454 00455 std::string GtpCommand::ArgLine() const 00456 { 00457 string result = m_line.substr(m_arguments[0].m_end); 00458 Trim(result); 00459 return result; 00460 } 00461 00462 string GtpCommand::ArgToLower(std::size_t number) const 00463 { 00464 string value = Arg(number); 00465 for (size_t i = 0; i < value.length(); ++i) 00466 value[i] = char(tolower(value[i])); 00467 return value; 00468 } 00469 00470 bool GtpCommand::BoolArg(std::size_t number) const 00471 { 00472 return Arg<bool>(number); 00473 } 00474 00475 void GtpCommand::CheckNuArg(std::size_t number) const 00476 { 00477 if (NuArg() == number) 00478 return; 00479 if (number == 0) 00480 throw GtpFailure() << "no arguments allowed"; 00481 else if (number == 1) 00482 throw GtpFailure() << "command needs one argument"; 00483 else 00484 throw GtpFailure() << "command needs " << number << " arguments"; 00485 } 00486 00487 void GtpCommand::CheckNuArgLessEqual(std::size_t number) const 00488 { 00489 if (NuArg() <= number) 00490 return; 00491 if (number == 1) 00492 throw GtpFailure() << "command needs at most one argument"; 00493 else 00494 throw GtpFailure() << "command needs at most " << number << " arguments"; 00495 } 00496 00497 double GtpCommand::FloatArg(std::size_t number) const 00498 { 00499 return Arg<double>(number); 00500 } 00501 00502 int GtpCommand::IntArg(std::size_t number) const 00503 { 00504 return Arg<int>(number); 00505 } 00506 00507 int GtpCommand::IntArg(std::size_t number, int min) const 00508 { 00509 return ArgMin<int>(number, min); 00510 } 00511 00512 int GtpCommand::IntArg(std::size_t number, int min, int max) const 00513 { 00514 return ArgMinMax<int>(number, min, max); 00515 } 00516 00517 void GtpCommand::Init(const string& line) 00518 { 00519 m_line = line; 00520 Trim(m_line); 00521 SplitLine(m_line); 00522 assert(m_arguments.size() > 0); 00523 ParseCommandId(); 00524 assert(m_arguments.size() > 0); 00525 m_response.str(""); 00526 m_response.copyfmt(s_dummy); 00527 } 00528 00529 void GtpCommand::ParseCommandId() 00530 { 00531 m_id = ""; 00532 if (m_arguments.size() < 2) 00533 return; 00534 istringstream in(m_arguments[0].m_value); 00535 int id; 00536 in >> id; 00537 if (in) 00538 { 00539 m_id = m_arguments[0].m_value; 00540 m_arguments.erase(m_arguments.begin()); 00541 } 00542 } 00543 00544 string GtpCommand::RemainingLine(std::size_t number) const 00545 { 00546 size_t index = number + 1; 00547 if (number >= NuArg()) 00548 throw GtpFailure() << "missing argument " << index; 00549 string result = m_line.substr(m_arguments[index].m_end); 00550 Trim(result); 00551 return result; 00552 } 00553 00554 void GtpCommand::SetResponse(const string& response) 00555 { 00556 m_response.str(response); 00557 } 00558 00559 void GtpCommand::SetResponseBool(bool value) 00560 { 00561 m_response.str(value ? "true" : "false"); 00562 } 00563 00564 std::size_t GtpCommand::SizeTypeArg(std::size_t number) const 00565 { 00566 return Arg<size_t>(number); 00567 } 00568 00569 std::size_t GtpCommand::SizeTypeArg(std::size_t number, std::size_t min) const 00570 { 00571 return ArgMin<size_t>(number, min); 00572 } 00573 00574 /** Split line into arguments. 00575 Arguments are words separated by whitespaces. 00576 Arguments with whitespaces can be quoted with quotation marks ('"'). 00577 Characters can be escaped with a backslash ('\'). 00578 @param line The line to split. */ 00579 void GtpCommand::SplitLine(const string& line) 00580 { 00581 m_arguments.clear(); 00582 bool escape = false; 00583 bool inString = false; 00584 ostringstream element; 00585 for (size_t i = 0; i < line.size(); ++i) 00586 { 00587 char c = line[i]; 00588 if (c == '"' && ! escape) 00589 { 00590 if (inString) 00591 { 00592 m_arguments.push_back(Argument(element.str(), i + 1)); 00593 element.str(""); 00594 } 00595 inString = ! inString; 00596 } 00597 else if (isspace(c) && ! inString) 00598 { 00599 if (! element.str().empty()) 00600 { 00601 m_arguments.push_back(Argument(element.str(), i + 1)); 00602 element.str(""); 00603 } 00604 } 00605 else 00606 element << c; 00607 escape = (c == '\\' && ! escape); 00608 } 00609 if (! element.str().empty()) 00610 m_arguments.push_back(Argument(element.str(), line.size())); 00611 } 00612 00613 //---------------------------------------------------------------------------- 00614 00615 GtpCallbackBase::~GtpCallbackBase() throw() 00616 { 00617 } 00618 00619 //---------------------------------------------------------------------------- 00620 00621 GtpEngine::GtpEngine() 00622 : m_quit(false) 00623 { 00624 Register("known_command", &GtpEngine::CmdKnownCommand, this); 00625 Register("list_commands", &GtpEngine::CmdListCommands, this); 00626 Register("name", &GtpEngine::CmdName, this); 00627 Register("protocol_version", &GtpEngine::CmdProtocolVersion, this); 00628 Register("quit", &GtpEngine::CmdQuit, this); 00629 Register("version", &GtpEngine::CmdVersion, this); 00630 } 00631 00632 GtpEngine::~GtpEngine() 00633 { 00634 typedef CallbackMap::iterator Iterator; 00635 for (Iterator i = m_callbacks.begin(); i != m_callbacks.end(); ++i) 00636 { 00637 delete i->second; 00638 #ifndef NDEBUG 00639 i->second = 0; 00640 #endif 00641 } 00642 } 00643 00644 void GtpEngine::BeforeHandleCommand() 00645 { 00646 // Default implementation does nothing 00647 } 00648 00649 void GtpEngine::BeforeWritingResponse() 00650 { 00651 // Default implementation does nothing 00652 } 00653 00654 /** Return @c true if command is known, @c false otherwise. */ 00655 void GtpEngine::CmdKnownCommand(GtpCommand& cmd) 00656 { 00657 cmd.SetResponseBool(IsRegistered(cmd.Arg())); 00658 } 00659 00660 /** List all known commands. */ 00661 void GtpEngine::CmdListCommands(GtpCommand& cmd) 00662 { 00663 cmd.CheckArgNone(); 00664 typedef CallbackMap::const_iterator Iterator; 00665 for (Iterator i = m_callbacks.begin(); i != m_callbacks.end(); ++i) 00666 cmd << i->first << '\n'; 00667 } 00668 00669 /** Return name. */ 00670 void GtpEngine::CmdName(GtpCommand& cmd) 00671 { 00672 cmd.CheckArgNone(); 00673 cmd << "Unknown"; 00674 } 00675 00676 /** Return protocol version. */ 00677 void GtpEngine::CmdProtocolVersion(GtpCommand& cmd) 00678 { 00679 cmd.CheckArgNone(); 00680 cmd << "2"; 00681 } 00682 00683 /** Quit command loop. */ 00684 void GtpEngine::CmdQuit(GtpCommand& cmd) 00685 { 00686 cmd.CheckArgNone(); 00687 SetQuit(); 00688 } 00689 00690 /** Return empty version string. 00691 The GTP standard says to return empty string, if no meaningful reponse 00692 is available. */ 00693 void GtpEngine::CmdVersion(GtpCommand& cmd) 00694 { 00695 cmd.CheckArgNone(); 00696 } 00697 00698 string GtpEngine::ExecuteCommand(const string& cmdline, ostream& log) 00699 { 00700 if (! IsCommandLine(cmdline)) 00701 throw GtpFailure() << "Bad command: " << cmdline; 00702 GtpCommand cmd; 00703 cmd.Init(cmdline); 00704 log << cmd.Line() << '\n'; 00705 GtpOutputStream gtpLog(log); 00706 bool status = HandleCommand(cmd, gtpLog); 00707 string response = cmd.Response(); 00708 if (! status) 00709 throw GtpFailure() << "Executing " << cmd.Line() << " failed"; 00710 return response; 00711 } 00712 00713 void GtpEngine::ExecuteFile(const string& name, ostream& log) 00714 { 00715 ifstream in(name.c_str()); 00716 if (! in) 00717 throw GtpFailure() << "Cannot read " << name; 00718 string line; 00719 GtpCommand cmd; 00720 GtpOutputStream gtpLog(log); 00721 while (getline(in, line)) 00722 { 00723 if (! IsCommandLine(line)) 00724 continue; 00725 cmd.Init(line); 00726 log << cmd.Line() << '\n'; 00727 00728 bool status = HandleCommand(cmd, gtpLog); 00729 if (! status) 00730 throw GtpFailure() << "Executing " << cmd.Line() << " failed"; 00731 } 00732 } 00733 00734 bool GtpEngine::HandleCommand(GtpCommand& cmd, GtpOutputStream& out) 00735 { 00736 BeforeHandleCommand(); 00737 bool status = true; 00738 string response; 00739 try 00740 { 00741 CallbackMap::const_iterator pos = m_callbacks.find(cmd.Name()); 00742 if (pos == m_callbacks.end()) 00743 { 00744 status = false; 00745 response = "unknown command: " + cmd.Name(); 00746 } 00747 else 00748 { 00749 GtpCallbackBase* callback = pos->second; 00750 (*callback)(cmd); 00751 response = cmd.Response(); 00752 } 00753 } 00754 catch (const GtpFailure& failure) 00755 { 00756 status = false; 00757 response = failure.Response(); 00758 } 00759 response = ReplaceEmptyLines(response); 00760 BeforeWritingResponse(); 00761 ostringstream ostr; 00762 ostr << (status ? '=' : '?') << cmd.ID() << ' ' << response; 00763 size_t size = response.size(); 00764 if (size == 0 || response[size - 1] != '\n') 00765 ostr << '\n'; 00766 ostr << '\n' << flush; 00767 out.Write(ostr.str()); 00768 out.Flush(); 00769 return status; 00770 } 00771 00772 bool GtpEngine::IsRegistered(const string& command) const 00773 { 00774 return (m_callbacks.find(command) != m_callbacks.end()); 00775 } 00776 00777 void GtpEngine::MainLoop(GtpInputStream& in, GtpOutputStream& out) 00778 { 00779 m_quit = false; 00780 #if GTPENGINE_PONDER 00781 PonderThread ponderThread(*this); 00782 #endif 00783 #if GTPENGINE_INTERRUPT 00784 ReadThread readThread(in, *this); 00785 #endif 00786 GtpCommand cmd; 00787 while (true) 00788 { 00789 #if GTPENGINE_PONDER 00790 ponderThread.StartPonder(); 00791 #endif 00792 #if GTPENGINE_INTERRUPT 00793 bool isStreamGood = readThread.ReadCommand(cmd); 00794 #else 00795 bool isStreamGood = ReadCommand(cmd, in); 00796 #endif 00797 #if GTPENGINE_PONDER 00798 ponderThread.StopPonder(); 00799 #endif 00800 if (isStreamGood) 00801 HandleCommand(cmd, out); 00802 else 00803 SetQuit(); 00804 if (m_quit) 00805 { 00806 #if GTPENGINE_PONDER 00807 ponderThread.Quit(); 00808 #endif 00809 #if GTPENGINE_INTERRUPT 00810 readThread.JoinThread(); 00811 #endif 00812 break; 00813 } 00814 } 00815 } 00816 00817 void GtpEngine::Register(const string& command, GtpCallbackBase* callback) 00818 { 00819 CallbackMap::iterator pos = m_callbacks.find(command); 00820 if (pos != m_callbacks.end()) 00821 { 00822 delete pos->second; 00823 #ifndef NDEBUG 00824 pos->second = 0; 00825 #endif 00826 m_callbacks.erase(pos); 00827 } 00828 m_callbacks.insert(make_pair(command, callback)); 00829 } 00830 00831 void GtpEngine::SetQuit() 00832 { 00833 m_quit = true; 00834 } 00835 00836 bool GtpEngine::IsQuitSet() const 00837 { 00838 return m_quit; 00839 } 00840 00841 00842 #if GTPENGINE_PONDER 00843 00844 void GtpEngine::Ponder() 00845 { 00846 // Default implementation does nothing 00847 #ifdef GTPENGINE_TEST 00848 cerr << "GtpEngine::Ponder()\n"; 00849 #endif 00850 } 00851 00852 void GtpEngine::StopPonder() 00853 { 00854 // Default implementation does nothing 00855 #ifdef GTPENGINE_TEST 00856 cerr << "GtpEngine::StopPonder()\n"; 00857 #endif 00858 } 00859 00860 void GtpEngine::InitPonder() 00861 { 00862 // Default implementation does nothing 00863 #ifdef GTPENGINE_TEST 00864 cerr << "GtpEngine::InitPonder()\n"; 00865 #endif 00866 } 00867 00868 #endif // GTPENGINE_PONDER 00869 00870 00871 #if GTPENGINE_INTERRUPT 00872 00873 void GtpEngine::Interrupt() 00874 { 00875 // Default implementation does nothing 00876 #ifdef GTPENGINE_TEST 00877 cerr << "GtpEngine::Interrupt()\n"; 00878 #endif 00879 } 00880 00881 #endif // GTPENGINE_INTERRUPT 00882 00883 //---------------------------------------------------------------------------- 00884 00885 #ifdef GTPENGINE_MAIN 00886 00887 /** Main routine for testing and standalone compilation. */ 00888 int main() 00889 { 00890 try 00891 { 00892 GtpEngine engine(cin, cout); 00893 engine.MainLoop(); 00894 } 00895 catch (const exception& e) 00896 { 00897 cerr << e.what() << '\n'; 00898 return 1; 00899 } 00900 return 0; 00901 } 00902 00903 #endif // GTPENGINE_MAIN 00904 00905 //----------------------------------------------------------------------------