Index   Main   Namespaces   Classes   Hierarchy   Annotated   Files   Compound   Global   Pages  

GtpEngine.cpp

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


17 Jun 2010 Doxygen 1.4.7