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 #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 //----------------------------------------------------------------------------


Sun Mar 13 2011 Doxygen 1.7.1