PLearn 0.1
Object.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // PLearn (A C++ Machine Learning Library)
00004 // Copyright (C) 1998 Pascal Vincent
00005 // Copyright (C) 1999-2002 Pascal Vincent, Yoshua Bengio and University of Montreal
00006 // Copyright (C) 2002 Frederic Morin
00007 // Copyright (C) 2007 Xavier Saint-Mleux, ApSTAT Technologies inc.
00008 
00009 // Redistribution and use in source and binary forms, with or without
00010 // modification, are permitted provided that the following conditions are met:
00011 // 
00012 //  1. Redistributions of source code must retain the above copyright
00013 //     notice, this list of conditions and the following disclaimer.
00014 // 
00015 //  2. Redistributions in binary form must reproduce the above copyright
00016 //     notice, this list of conditions and the following disclaimer in the
00017 //     documentation and/or other materials provided with the distribution.
00018 // 
00019 //  3. The name of the authors may not be used to endorse or promote
00020 //     products derived from this software without specific prior written
00021 //     permission.
00022 // 
00023 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
00024 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00025 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
00026 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00027 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00028 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00029 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00030 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00031 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00032 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 // 
00034 // This file is part of the PLearn library. For more information on the PLearn
00035 // library, go to the PLearn Web site at www.plearn.org
00036 
00037 
00038 /* *******************************************************      
00039  * $Id: Object.cc 9940 2009-02-16 22:29:22Z nouiz $
00040  * AUTHORS: Pascal Vincent & Yoshua Bengio
00041  * This file is part of the PLearn library.
00042  ******************************************************* */
00043 
00044 #include "Object.h"
00045 #include "stringutils.h"    
00046 #include <plearn/io/fileutils.h>
00047 #include <plearn/io/pl_log.h>
00048 #include <plearn/io/load_and_save.h>
00049 #include <plearn/io/openFile.h>
00050 #include <plearn/io/openString.h>
00051 #include "TypeFactory.h"
00052 #include "RemoteDeclareMethod.h"
00053 #include <algorithm>
00054 
00055 namespace PLearn {
00056 using namespace std;
00057 
00058 PLEARN_IMPLEMENT_OBJECT(
00059     Object,
00060     "Base class for all high-level PLearn objects.",
00061     "Object exposes simple mechanisms for:\n"
00062     "\n"
00063     "- automatic memory management (through reference counting and smart pointers)\n"
00064     "- serialization (read, write, save, load)\n"
00065     "- runtime type information (classname)\n"
00066     "- displaying (info, print)\n"
00067     "- deep copying (deepCopy)\n"
00068     "- remote method calling mechanism (call(), declareMethods())\n"
00069     "- a generic way of setting options (setOption) when not knowing the\n"
00070     "  exact type of the Object and a generic build() method (the combination\n"
00071     "  of the two allows to change the object structure and rebuild it at\n"
00072     "  runtime)\n"
00073     "\n"
00074     "\n"
00075     "NOTES ON THE PLEARN OBJECT MODEL.  All \"significant\" PLearn classes inherit\n"
00076     "from this class, PLearn::Object, as their ultimate base.  Each PLearn class\n"
00077     "defines a number of \"options\", which are data fields that are reflected in\n"
00078     "the serialized representation of the object.  Hence, the set of options\n"
00079     "describes the capabilities of an object from the viewpoint of the script\n"
00080     "writer.  The meaning of each option is documented within each class.\n"
00081     "\n"
00082     "Moreover, all options are given a set of flags.  The meaning of each flag\n"
00083     "is as follows:\n"
00084     "\n"
00085     "- buildoption : an option typically specified before calling the\n"
00086     "  initial build (semantically similar to a constructor parameter), for\n"
00087     "  instance the number of hidden units in a neural net.\n"
00088     "\n"
00089     "- learntoption : n option whose proper value is computed by the class\n"
00090     "  after construction (not to be set by the user before build) and potentially\n"
00091     "  complex operations such as learning over a training set.  Example: the\n"
00092     "  (trained) weights of a neural net.\n"
00093     "\n"
00094     "- tuningoption : an option typically set after the initial build, to tune\n"
00095     "  the object\n"
00096     "\n"
00097     "- nosave : when set, this flag requests the option not to be saved in\n"
00098     "  the object serialisation; mostly used when we must have several options\n"
00099     "  that map to the same physical data field, for script backward\n"
00100     "  compatibility.\n"
00101     "\n"
00102     "- nonparentable : when this flag is set, the option does not lead to a\n"
00103     "  parenting relationship in the \"ParentableObject\" sense.  In other words,\n"
00104     "  the object pointed to by this option does not get its parent() backpointer\n"
00105     "  set to this. (See the ParentableObject class for details).\n"
00106     "\n"
00107     "- nontraversable : when this flag is set, the option is not traversed\n"
00108     "  by the ObjectGraphIterator class (and ipso facto by related functions, such\n"
00109     "  as memfun_broadcast. (See the ObjectGraphIterator class for details).\n"
00110     );
00111 
00112 
00113 //#####  Basic PLearn::Object Protocol  #######################################
00114 
00115 Object::Object(bool call_build_)
00116 {
00117     if (call_build_)
00118         build_();
00119 }
00120 
00121 Object::~Object()
00122 { }
00123 
00124 // by default, do nothing...
00125 void Object::makeDeepCopyFromShallowCopy(CopiesMap& copies)
00126 { }
00127 
00128 void Object::build_()
00129 { }
00130 
00131 void Object::build()
00132 { }
00133 
00134 string Object::info() const
00135 {
00136     return classname();
00137 }
00138 
00139 string Object::asString() const
00140 {
00141     string s;
00142     PStream out= openString(s, PStream::plearn_ascii, "w");
00143     out << this;
00144     out.flush();
00145     return removeblanks(s);
00146 }
00147 
00148 string Object::asStringRemoteTransmit() const
00149 {
00150     string s;
00151     PStream out= openString(s, PStream::plearn_ascii, "w");
00152     out.remote_plearn_comm= true;
00153     out << this;
00154     out.flush();
00155     return removeblanks(s);
00156 }
00157 
00158 //#####  Option-Manipulation Functions  #######################################
00159 
00160 void Object::setOption(const string& optionname, const string& value)
00161 {
00162     PStream in = openString(value, PStream::plearn_ascii);
00163     readOptionVal(in, optionname);
00164 }
00165 
00166 #ifdef PL_PYTHON_VERSION 
00167 void Object::setOptionFromPython(const string& optionname, const PythonObjectWrapper& value)
00168 {
00169     OptionMap& om= getOptionMap();
00170     OptionMap::iterator it= om.find(optionname);
00171     if(it == om.end())
00172         PLERROR("%s has no option %s.", classname().c_str(), optionname.c_str());
00173     it->second->setFromPythonObject(this, value);
00174 }
00175 #endif //def PL_PYTHON_VERSION 
00176 
00177 bool Object::hasOption(const string &optionname) const
00178 { 
00179     OptionMap& om= getOptionMap();
00180     OptionMap::iterator it= om.find(optionname);
00181     return it != om.end();
00182 }
00183 
00184 string Object::getOption(const string &optionname) const
00185 { 
00186     string s;
00187     PStream out = openString(s, PStream::plearn_ascii, "w");
00188     writeOptionVal(out, optionname);
00189     out.flush();
00190     return removeblanks(s);
00191 }
00192 
00193 void Object::changeOptions(const map<string,string>& name_value)
00194 {
00195     map<string,string>::const_iterator it = name_value.begin();
00196     map<string,string>::const_iterator itend = name_value.end();
00197     while(it!=itend)
00198     {
00199         setOption(it->first, it->second);
00200         ++it;
00201     }
00202 }
00203 
00204 void Object::changeOption(const string& optionname, const string& value)
00205 {
00206     map<string,string> name_value;
00207     name_value[optionname] = value;
00208     changeOptions(name_value);
00209 }
00210 
00211 
00212 //#####  Object::readOptionVal  ###############################################
00213 
00214 void Object::readOptionVal(PStream &in, const string &optionname, unsigned int id)
00215 {
00216     try 
00217     {
00218 
00219         OptionMap& om= getOptionMap();
00220         OptionMap::iterator it= om.find(optionname);
00221         if(it != om.end())
00222         {
00223             it->second->read(this, in);
00224             return;
00225         }
00226 
00227         // Found no options matching 'optionname'. First look for brackets. If there
00228         // are brackets, they must be located before any dot.
00229         size_t lb_pos = optionname.find('[');
00230         size_t rb_pos = optionname.find(']');
00231         size_t dot_pos = optionname.find('.');
00232         if (rb_pos != string::npos) {
00233             if (lb_pos == string::npos)
00234                 PLERROR("Object::readOptionVal() - Unmatched brackets");
00235             string optname = optionname.substr(0, lb_pos);
00236 
00237             // Found no dot, or a right bracket before a dot
00238             if (dot_pos == string::npos || rb_pos < dot_pos) {
00239                 string index = optionname.substr(lb_pos + 1, rb_pos - lb_pos - 1);
00240 
00241                 it= om.find(optname);
00242                 if(it != om.end())
00243                 {
00244                     // There are two cases here: either there is a dot located
00245                     // immediately after the right bracket, or there is no dot.
00246                     // If there is a dot, the option HAS to be an Object
00247                     if (dot_pos != string::npos && dot_pos == rb_pos+1) {
00248                         int i = toint(index);
00249                         it->second->getIndexedObject(this, i)->readOptionVal(
00250                             in, optionname.substr(dot_pos + 1));
00251                     }
00252                     else if (dot_pos == string::npos)
00253                         it->second->readIntoIndex(this, in, index);
00254                     else
00255                         PLERROR("Object::readOptionVal() - unknown option format \"%s\"",
00256                                 optionname.c_str());
00257                     return;
00258                 }
00259             }
00260         }
00261         else if (lb_pos != string::npos)
00262             PLERROR("Object::readOptionVal() - Unmatched brackets");
00263 
00264         // No brackets, look for a dot
00265         if (dot_pos != string::npos) 
00266         {
00267             // Found a dot, assume it's a field with an Object * field
00268             string optname = optionname.substr(0, dot_pos);
00269             string optoptname = optionname.substr(dot_pos + 1);
00270             it= om.find(optname);
00271             if(it != om.end())
00272             {
00273                 it->second->getAsObject(this)->readOptionVal(in, optoptname);
00274                 return;
00275             }
00276         }
00277     }
00278     catch(const PLearnError& e)
00279     { 
00280         if(id == UINT_MAX)
00281             PLERROR("Problem while attempting to read value of option \"%s\" of a \"%s\":\n %s", 
00282                     optionname.c_str(), classname().c_str(), e.message().c_str());
00283         else
00284             PLERROR("Problem while reading ptr %d while attempting to read value of option \"%s\" of a \"%s\":\n %s", 
00285                     id, optionname.c_str(), classname().c_str(), e.message().c_str());
00286             
00287     }
00288 
00289     // There are bigger problems in the world but still it isn't always funny
00290     if(optionname.empty())
00291         PLERROR("Found an empty option name in a \"%s\". "
00292                 "Maybe you forgot a ')'?", classname().c_str());
00293     PLERROR("There is no option named \"%s\" in a \"%s\"",
00294             optionname.c_str(),classname().c_str());
00295 }
00296 
00297 
00298 //#####  Object::writeOptionVal  ##############################################
00299 
00300 void Object::writeOptionVal(PStream &out, const string &optionname) const
00301 {
00302     OptionMap& om= getOptionMap();
00303     OptionMap::iterator it= om.find(optionname);
00304     if(it != om.end())
00305     {
00306         it->second->write(this, out);
00307         return;
00308     }
00309     
00310     // Found no options matching 'optionname'. First look for brackets. If there
00311     // are brackets, they must be located before any dot.
00312     size_t lb_pos = optionname.find('[');
00313     size_t rb_pos = optionname.find(']');
00314     size_t dot_pos = optionname.find('.');
00315     if (rb_pos != string::npos) {
00316         if (lb_pos == string::npos)
00317             PLERROR("Object::writeOptionVal() - Unmatched brackets");
00318         string optname = optionname.substr(0, lb_pos);
00319 
00320         // Found no dot, or a right bracket before a dot
00321         if (dot_pos == string::npos || rb_pos < dot_pos) {
00322             string index = optionname.substr(lb_pos + 1, rb_pos - lb_pos - 1);
00323             it= om.find(optname);
00324             if(it != om.end())
00325             {
00326                 // There are two cases here: either there is a dot located
00327                 // immediately after the right bracket, or there is no dot.  If
00328                 // there is a dot, the option HAS to be an Object
00329                 if (dot_pos != string::npos && dot_pos == rb_pos+1) {
00330                     int i = toint(index);
00331                     it->second->getIndexedObject(this, i)->writeOptionVal(
00332                         out, optionname.substr(dot_pos + 1));
00333                 }
00334                 else if (dot_pos == string::npos)
00335                     it->second->writeAtIndex(this, out, index);
00336                 else
00337                     PLERROR("Object::writeOptionVal() - unknown option format \"%s\"",
00338                             optionname.c_str());
00339                 return;
00340             }
00341         }
00342     }
00343     else if (lb_pos != string::npos)
00344         PLERROR("Object::writeOptionVal() - Unmatched brackets");
00345 
00346     // No brackets, look for a dot
00347     if (dot_pos != string::npos) {
00348         // Found a dot, assume it's a field with an Object * field
00349         string optname = optionname.substr(0, dot_pos);
00350         string optoptname = optionname.substr(dot_pos + 1);
00351         it= om.find(optname);
00352         if(it != om.end())
00353         {
00354             it->second->getAsObject(this)->writeOptionVal(out, optoptname);
00355             return;
00356         }
00357     }
00358     // There are bigger problems in the world but still it isn't always funny
00359     PLERROR("Object::writeOptionVal() - Unknown option \"%s\"", optionname.c_str());    
00360 }
00361 
00362 
00363 //#####  Object::parseOptionName  #############################################
00364 
00365 bool Object::parseOptionName(const string& optionname,
00366                              Object*& final_object,
00367                              OptionList::iterator& option_iter,
00368                              string& option_index)
00369 {
00370     OptionList &options = getOptionList();
00371     for (OptionList::iterator it = options.begin(); it != options.end(); ++it)
00372         if ((*it)->optionname() == optionname) {
00373             final_object = this;
00374             option_iter  = it;
00375             option_index = "";
00376             return true;
00377         }
00378 
00379     // Found no options matching 'optionname'. First look for brackets. If there
00380     // are brackets, they must be located before any dot.
00381     size_t lb_pos = optionname.find('[');
00382     size_t rb_pos = optionname.find(']');
00383     size_t dot_pos = optionname.find('.');
00384     if (rb_pos != string::npos) {
00385         if (lb_pos == string::npos)
00386             PLERROR("Object::parseOptionName() - Unmatched brackets when parsing option \"%s\"",
00387                     optionname.c_str());
00388         string optname = optionname.substr(0, lb_pos);
00389 
00390         // Found no dot, or a right bracket before a dot
00391         if (dot_pos == string::npos || rb_pos < dot_pos) {
00392             string index = optionname.substr(lb_pos + 1, rb_pos - lb_pos - 1);
00393             for (OptionList::iterator it = options.begin(); it != options.end(); ++it)
00394                 if ((*it)->optionname() == optname) {
00395                     // There are two cases here: either there is a dot located
00396                     // immediately after the right bracket, or there is no dot.  If
00397                     // there is a dot, the option HAS to be an Object
00398                     if (dot_pos != string::npos && dot_pos == rb_pos+1) {
00399                         int i = toint(index);
00400                         return (*it)->getIndexedObject(this, i)->
00401                             parseOptionName(optionname.substr(dot_pos + 1),
00402                                             final_object, option_iter, option_index);
00403                     }
00404                     else if (dot_pos == string::npos) {
00405                         final_object = this;
00406                         option_iter  = it;
00407                         option_index = index;
00408                         return true;
00409                     }
00410                     else {
00411                         PLERROR("Object::writeOptionVal() - unknown option format \"%s\"",
00412                                 optionname.c_str());
00413                         return false;        
00414                     }
00415                 }
00416         }
00417     }
00418     else if (lb_pos != string::npos)
00419         PLERROR("Object::writeOptionVal() - Unmatched brackets when parsing option \"%s\"",
00420                 optionname.c_str());
00421 
00422     // No brackets, look for a dot
00423     if (dot_pos != string::npos) {
00424         // Found a dot, assume it's a field with an Object * field
00425         string optname = optionname.substr(0, dot_pos);
00426         string optoptname = optionname.substr(dot_pos + 1);
00427         for (OptionList::iterator it = options.begin(); it != options.end(); ++it)
00428             if ((*it)->optionname() == optname) {
00429                 return (*it)->getAsObject(this)->
00430                     parseOptionName(optoptname, final_object, option_iter, option_index);
00431             }
00432     }
00433 
00434     // Set reasonable defaults for an error condition
00435     final_object = 0;
00436     option_iter  = OptionList::iterator();
00437     option_index = "";
00438     return false;
00439 }
00440 
00441 
00442 bool Object::parseOptionName(const string& optionname,
00443                              const Object*& final_object,
00444                              OptionList::iterator& option_iter,
00445                              string& option_index) const
00446 {
00447     return const_cast<Object*>(this)->parseOptionName(
00448         optionname, const_cast<Object*&>(final_object),
00449         option_iter, option_index);
00450 }
00451     
00452 
00453 //#####  Object::getOptionsToSave  ############################################
00454 
00455 string Object::getOptionsToSave() const
00456 {
00457     string res = "";
00458     OptionList& options = getOptionList();
00459   
00460     for( OptionList::iterator it = options.begin(); it!=options.end(); ++it )
00461     {
00462         OptionBase::flag_t flags = (*it)->flags();
00463         if(!(flags & OptionBase::nosave))
00464             res += (*it)->optionname() + " ";
00465     }
00466     return res;
00467 }
00468 
00469 
00470 //#####  Object::getOptionsToRemoteTransmit  ############################################
00471 string Object::getOptionsToRemoteTransmit() const
00472 {
00473     string res = "";
00474     OptionList& options = getOptionList();
00475   
00476     for( OptionList::iterator it = options.begin(); it!=options.end(); ++it )
00477     {
00478         OptionBase::flag_t flags = (*it)->flags();
00479         if(!(flags & OptionBase::nosave) || flags & OptionBase::remotetransmit)
00480             res += (*it)->optionname() + " ";
00481     }
00482     return res;
00483 }
00484 
00485 //#####  Object::newread  #####################################################
00486 
00487 void Object::newread(PStream &in, unsigned int id)
00488 {
00489     PP<Object> dummy_obj = 0; // Used to read skipped options.
00490     string cl;
00491 
00492     // Allow the use of the pointer syntax for non-pointer instances.
00493     in.skipBlanksAndComments();
00494     if ( in.peek() == '*' )  
00495     {
00496         const char* errmsg = "In Object::newread(PStream&) Wrong format. "
00497             "Expecting \"*%d->\" but got \"*%d%c%c\".";
00498     
00499         in.get(); // Eat '*'
00500         unsigned int id;
00501         in >> id;
00502         in.skipBlanksAndComments();
00503 
00504         char dash = in.get(); // Eat '-'
00505         if ( dash != '-' )
00506         {
00507             if ( dash == ';' )
00508                 PLERROR("In Object::newread(PStream&): Non pointer objects can be prefixed with dummy "
00509                         "references ('*%d ->') but these references MUST NOT BE USED afterwards. Just "
00510                         "read '*%d;'", id, id );
00511             PLERROR( errmsg, id, id, dash, in.get() );
00512         }
00513     
00514         char cc = in.get();
00515         if(cc != '>') // Eat '>'
00516             PLERROR( errmsg, id, id, dash, cc);
00517         in.skipBlanksAndCommentsAndSeparators();    
00518     }
00519     
00520     in.getline(cl, '(');
00521     cl = removeblanks(cl);
00522     if (cl != classname())
00523         PLERROR("Object::newread() - Was expecting \"%s\", but read \"%s\"",
00524                 classname().c_str(), cl.c_str());
00525 
00526     in.skipBlanksAndComments();
00527     int c = in.get();
00528     if (c != ')') 
00529     {
00530         in.putback(c);
00531         for (;;) 
00532         {
00533             // Read all specified options
00534             string optionname;
00535             in.getline(optionname, '=');
00536             optionname = removeblanks(optionname);
00537             in.skipBlanksAndComments();
00538             OptionMap& om= getOptionMap();
00539             OptionMap::iterator it= om.find(optionname);
00540             if(it != om.end() && it->second->shouldBeSkipped())
00541             {
00542                 // Create a dummy object that will read this option.
00543                 if (!dummy_obj) {
00544                     // Note that we do not call build on 'dummy_obj'. This is
00545                     // because some classes may crash when build is called
00546                     // before setting options (though this is not a desired
00547                     // behaviour, it can be hard to figure out what is going on
00548                     // when it crashes here).
00549                     dummy_obj =
00550                         TypeFactory::instance().newObject(this->classname());
00551                 }
00552                 dummy_obj->readOptionVal(in, optionname, id);
00553             }
00554             else
00555             {
00556                 // cerr << "Reading option: " << optionname << endl;
00557                 readOptionVal(in, optionname, id);
00558                 // cerr << "returned from reading optiion " << optionname << endl;
00559             }
00560             in.skipBlanksAndCommentsAndSeparators();
00561 
00562             if (in.peek() == ')') 
00563             {
00564                 in.get();
00565                 break;
00566             }
00567         }
00568     }
00569     build(); // Build newly read Object
00570 }
00571 
00572 
00573 //#####  Object::newwrite  ####################################################
00574 
00575 void Object::newwrite(PStream& out) const
00576 {
00577     vector<string> optnames= split(
00578         out.remote_plearn_comm?
00579         getOptionsToRemoteTransmit():
00580         getOptionsToSave());
00581 
00582     //we swap the VMatrix option at the end for better lisibility of the file
00583     if (!optnames.empty())
00584         for(size_t i =0; i<optnames.size() - 1 ;i++)
00585         {
00586             if("source"==optnames[i] || "vm"==optnames[i])
00587             {
00588                 string tmp = optnames[i];
00589                 optnames[i] = optnames.back();
00590                 optnames.back() = tmp;
00591             }
00592         }
00593 
00594     out.write(classname());
00595     out.write("(\n");
00596     for (size_t i = 0; i < optnames.size(); ++i) 
00597     {
00598         out.write(optnames[i]);
00599         out.write(" = ");
00600         writeOptionVal(out, optnames[i]);
00601         if (i < optnames.size() - 1)
00602             out.write(";\n");
00603     }
00604 //    out.write(" ) # "+classname()+"\n");
00605     out.write(" )\n");
00606 }
00607 
00608 
00609 void Object::write(ostream& out_) const
00610 {
00611     PStream out(&out_);
00612     newwrite(out);
00613 }
00614 
00615 void Object::read(istream& in_)
00616 { 
00617     in_ >> ws; // skip blanks
00618     int c = in_.peek();
00619     if(c=='<') // --> it's an "old-style" <Classname> ... </Classname> kind of definition
00620         oldread(in_);
00621     else { // assume it's a "new-style" Classname(...) 
00622         PStream in(&in_);
00623         newread(in);
00624     }
00625 }
00626 
00627 
00628 //#####  Remote Method Invocation  ############################################
00629 
00630 void Object::prepareToSendResults(PStream& out, int nres)
00631 { 
00632     DBG_LOG << "PREPARING TO SEND " << nres << " RESULTS." << endl;
00633     out.write("!R "); out << nres; 
00634 }
00635 
00636 
00637 void Object::call(const string& methodname, int nargs, PStream& io)
00638 {
00639     // Look up methodname in the RemoteMethodMap
00640     RemoteMethodMap& rmm = getRemoteMethodMap();
00641     if (const RemoteTrampoline* trampoline = rmm.lookup(methodname,nargs))
00642         trampoline->call(this, nargs, io);
00643     else
00644         PLERROR("No method named '%s' taking %d arguments supported by objects of class '%s'.",
00645                 methodname.c_str(), nargs, classname().c_str());
00646 }
00647 
00648 
00649 // We use an anonymous namespace to ensure the following classes are local to
00650 // this specific translation unit.
00651 // Note that these classes have been moved out of the declareMethods Object
00652 // method as otherwise compilation will fail with Microsoft Visual C++.
00653 namespace {
00654 
00655         // The following are custom trampolines for the very special case where
00656         // no C++ function can be mapped to these remote functions, since their
00657         // return type is polymorphic.
00658         struct ObjectTrampolineGetOption : public RemoteTrampoline
00659         {
00660                 ObjectTrampolineGetOption(const string& methodname, const RemoteMethodDoc& doc)
00661                         : RemoteTrampoline(methodname, doc)
00662                 { }
00663 
00664                 virtual void call(Object* instance, int nargs, PStream& io) const
00665                 {
00666                         checkNargs(nargs, 1);
00667                         string optionname;
00668                         io >> optionname;
00669                         prepareToSendResults(io, 1);
00670                         instance->writeOptionVal(io, optionname);
00671                         io.flush();
00672                 }
00673 
00674 #ifdef PL_PYTHON_VERSION 
00675                 virtual PythonObjectWrapper call(Object* instance, 
00676                                                  const TVec<PythonObjectWrapper>& args) const
00677                 {
00678                     checkNargs(args.size(), 1);
00679                     string optionname= args[0];
00680                     OptionMap& om= instance->getOptionMap();
00681                     OptionMap::iterator it= om.find(optionname);
00682                     if(it != om.end())
00683                         return it->second->getAsPythonObject(instance);
00684                     PLERROR("in ObjectTrampolineGetOption::call (python ver.) : "
00685                             "unknown option: '%s'", optionname.c_str());
00686                     return PythonObjectWrapper();//gcc: shut up!
00687                 }
00688 #endif //def PL_PYTHON_VERSION 
00689         };
00690 
00691         struct ObjectTrampolineGetObject : public RemoteTrampoline
00692         {
00693                 ObjectTrampolineGetObject(const string& methodname, const RemoteMethodDoc& doc)
00694                         : RemoteTrampoline(methodname, doc)
00695                 { }
00696 
00697                 virtual void call(Object* instance, int nargs, PStream& io) const
00698                 {
00699                         checkNargs(nargs, 0);
00700                         prepareToSendResults(io, 1);
00701                         io << *instance;
00702                         io.flush();
00703                 }
00704 
00705 #ifdef PL_PYTHON_VERSION 
00706                 virtual PythonObjectWrapper call(Object* instance, 
00707                                                  const TVec<PythonObjectWrapper>& args) const
00708                 {
00709                         checkNargs(args.size(), 0);
00710                         return PythonObjectWrapper(instance);
00711                 }
00712 #endif //def PL_PYTHON_VERSION 
00713 
00714         };
00715 
00716 } // End of anonymous namespace.
00717 
00718 void Object::declareMethods(RemoteMethodMap& rmm)
00719 {
00720     declareMethod(rmm, "hasOption", &Object::hasOption,
00721                   (BodyDoc("Checks whether the object has an option with the given name"),
00722                    ArgDoc ("optionname","The name of the option looked for"),
00723                    RetDoc ("A bool that is true if the option was found.")));
00724 
00725     declareMethod(rmm, "changeOptions", &Object::changeOptions,
00726                   (BodyDoc("Change a set of options within the object"),
00727                    ArgDoc ("option_map",
00728                            "Set of option-name:option-value pairs to "
00729                            "modify within the object.")));
00730 
00731     declareMethod(rmm, "getOptionAsString", &Object::getOption,
00732                   (BodyDoc("Return a given object option in PLearn serialized representation."),
00733                    ArgDoc ("option_name", "Name of the option to get"),
00734                    RetDoc ("Object option in string form, or exception if "
00735                            "the option does not exist.")));
00736 
00737     declareMethod(rmm, "asString", &Object::asString,
00738                   (BodyDoc("Returns a string representation of this object."),
00739                    RetDoc ("string representation of the object")));
00740 
00741     declareMethod(rmm, "asStringRemoteTransmit", &Object::asStringRemoteTransmit,
00742                   (BodyDoc("Returns a string representation of this object,"
00743                            " including remotetransmit options."),
00744                    RetDoc ("string representation of the object (w/remotetransmit)")));
00745 
00746     declareMethod(rmm, "run", &Object::run,
00747                   (BodyDoc("Run the given object, if it is runnable; "
00748                            "raise an exception if it is not.")));
00749 
00750     declareMethod(rmm, "save", &Object::remote_save,
00751                   (BodyDoc("Save the given object to disk under the designated filename\n"),
00752                    ArgDoc ("filepath", "Pathname where the object should be saved"),
00753                    ArgDoc ("io_formatting",
00754                            "Format in which the object should be saved.  Can be one of:\n"
00755                            "- \"plearn_ascii\": use the PLearn ASCII (text) serialisation format\n"
00756                            "- \"plearn_binary\": use the PLearn binary format, which can be\n"
00757                            "  more efficient for large objects\n")));
00758 
00759     declareMethod(rmm, "build", &Object::build,
00760                   (BodyDoc("Build newly created object (after setting options).\n")));
00761 
00762     declareMethod(rmm, "classname", &Object::classname,
00763                   (BodyDoc("Returns the name of the class"),
00764                    RetDoc ("Class name as string")));
00765 
00766     declareMethod(rmm, "usage", &Object::usage,
00767                   (BodyDoc("Returns the refcount of this PPointable"),
00768                    RetDoc ("Number of references")));
00769 
00770     declareMethod(rmm, "deepCopyNoMap", &Object::deepCopyNoMap,
00771                   (BodyDoc("Returns a deep copy of the object"),
00772                    RetDoc ("Deep copy.")));
00773 
00774 #ifdef PL_PYTHON_VERSION 
00775     declareMethod(rmm, "pyDeepCopy", &Object::pyDeepCopy,
00776                   (BodyDoc("Returns a pair containing a deep copy of "
00777                            "the object and the updated copies map."),
00778                    ArgDoc ("copies", "The initial copies map"),
00779                    RetDoc ("Deep copy, copies map")));
00780 #endif //def PL_PYTHON_VERSION 
00781 
00782 #ifdef PL_PYTHON_VERSION 
00783     declareMethod(rmm, "setOptionFromPython", &Object::setOptionFromPython,
00784                   (BodyDoc("Change an option within the object from a PythonObjectWrapper"),
00785                    ArgDoc("optionname", "The name of the option to change."),
00786                    ArgDoc("value","the new value from python")));
00787 #endif //def PL_PYTHON_VERSION 
00788 
00789     rmm.insert(
00790         "getOption", 1,
00791         new ObjectTrampolineGetOption(
00792             "getOption",
00793             (BodyDoc("Return the option value given its name.  Note that the returned value\n"
00794                      "is POLYMORPHIC: its type varies according to the actual type of the\n"
00795                      "option."),
00796              ArgDoc ("optionname", "Name of the option to get"),
00797              RetDoc ("Value contained in the option"),
00798              ArgTypeDoc("string"),
00799              RetTypeDoc("(polymorphic)"))));
00800 
00801     rmm.insert(
00802         "getObject", 0,
00803         new ObjectTrampolineGetObject(
00804             "getObject",
00805             (BodyDoc("Return the object itself by value.  Note that the returned value\n"
00806                      "is POLYMORPHIC: its type varies according to the actual type of the\n"
00807                      "object."),
00808              RetDoc ("Object value"),
00809              RetTypeDoc("(polymorphic)"))));
00810 }
00811 
00812 
00813 //#####  Serialization and Miscellaneous  #####################################
00814 
00815 void Object::run()
00816 {
00817     PLERROR("%s::run() : Not a runnable Object", this->classname().c_str());
00818 }
00819 
00820 void Object::oldread(istream& in)
00821 {
00822     PLERROR("oldread method not implemented for this object");
00823 }
00824 
00825 void Object::save(const PPath& filename) const
00826 {
00827     PLWARNING("This method is deprecated. It simply calls the generic PLearn save function "
00828               "(that can save any PLearn object): PLearn::save(filename, *this) "
00829               "So you should call PLearn::save directly (it's defined in plearn/io/load_and_save.h).");
00830     PLearn::save(filename, *this);
00831 }
00832 
00833 void Object::load(const PPath& filename)
00834 {
00835     PLWARNING("This method is deprecated. It simply calls the generic PLearn load function "
00836               "(that can load any PLearn object): PLearn::load(filename, *this) "
00837               "So you should call PLearn::load directly (it's defined in plearn/io/load_and_save.h).");
00838     PLearn::load(filename, *this);
00839 }
00840 
00841 Object* Object::deepCopyNoMap()
00842 {
00843     CopiesMap cm;
00844     return deepCopy(cm);
00845 }
00846 
00847 #ifdef PL_PYTHON_VERSION 
00848 tuple<Object*, CopiesMap> Object::pyDeepCopy(CopiesMap& copies)
00849 {
00850     Object* o= deepCopy(copies);
00851     return make_tuple(o, copies);
00852 }
00853 #endif //def PL_PYTHON_VERSION 
00854 
00855 
00856 Object* loadObject(const PPath &filename)
00857 {
00858     PStream in = openFile(filename, PStream::plearn_ascii, "r");
00859     Object *o = readObject(in);
00860     o->build();
00861     return o;
00862 }
00863 
00864 Object* macroLoadObject(const PPath &filename, map<string, string>& vars)
00865 {
00866     time_t date = 0;
00867     string script = readFileAndMacroProcess(filename, vars, date);
00868     PStream sin = openString(script,PStream::plearn_ascii);
00869     Object* o = readObject(sin);
00870     o->build();
00871     return o;
00872 }
00873   
00874 Object* macroLoadObject(const PPath &filename)
00875 {
00876     map<string, string> vars;
00877     return macroLoadObject(filename,vars);
00878 }
00879 
00880 Object* readObject(PStream &in, unsigned int id)
00881 {
00882     Object *o=0;
00883     in.skipBlanksAndCommentsAndSeparators();
00884 
00885     string head;
00886 
00887     int c = in.peek();
00888     if (c == '<')  // Old (deprecated) serialization mode 
00889         PLERROR("Old deprecated serialization mode starting with '<' no longer supported.");
00890     else if (c == '*') // Pointer to object
00891     {
00892         in >> o;
00893     } 
00894     else if(c == '`') // back-quote: reference to an object in another file
00895     {
00896         in.get(); // skip the opening back-quote
00897         string fname;
00898         in.getline(fname,'`');
00899         fname = removeblanks(fname);
00900         // TODO: Check if this is really what we want
00901         //       (ie: We could want to use the options
00902         //            of 'in' to load the object...)
00903         o = loadObject(fname);
00904     }
00905     else // It must be a Classname(...) kind of definition 
00906     {
00907         string cl;
00908         in.getline(cl, '(');
00909         cl = removeblanks(cl);
00910         // It's a Classname(opt1 = ...; ...; optn = ...); --> calls newread()
00911         o = TypeFactory::instance().newObject(cl);
00912         if (!o)
00913             PLERROR("readObject() - Type \"%s\" not declared in TypeFactory map (did you do a proper DECLARE_NAME_AND_DEEPCOPY?)", cl.c_str());
00914         in.unread(cl+'(');
00915 
00916         // Finally read the guts of the object
00917         o->newread(in, id);
00918     }
00919 
00920 #if 0
00921     // Code that could be used... but need to see if it's useful and not too ugly.
00922     // See if we actually want an option of this object, instead of the object itself.
00923     in.skipBlanksAndCommentsAndSeparators();
00924     while (in.peek() == '.') {
00925         in.get(); // Skip the dot.
00926         char ch;
00927         string option_name;
00928         while (((ch = in.peek()) >= 'A' && ch <= 'z') || ch == '_' || (ch >= '0' && ch <= '9')) {
00929             in.get();
00930             option_name += ch;
00931         }
00932         if (option_name == "")
00933             PLERROR("In readObject - Could not read correctly the option name following a dot");
00934         o = newObject(o->getOption(option_name));
00935     }
00936 #endif
00937        
00938     if (id != UINT_MAX)
00939         in.copies_map_in[id] = o;
00940     return o;
00941 }
00942 
00943 
00944 PStream& operator>>(PStream& in, Object*& x)
00945 {
00946     in.skipBlanksAndCommentsAndSeparators();
00947     if (in.peek() == '*')
00948     {
00949         in.get(); // Eat '*'
00950         unsigned int id;
00951         in >> id;
00952         //don't skip blanks before we need to read something else (read might block).
00953         //in.skipBlanksAndCommentsAndSeparators();
00954         if (id==0)
00955             x = 0;
00956         else
00957         {
00958             in.skipBlanksAndCommentsAndSeparators(); 
00959             if (in.peek() == '-') 
00960             {
00961                 in.get(); // Eat '-'
00962                 char cc = in.get();
00963                 if(cc != '>') // Eat '>'
00964                     PLERROR("In PStream::operator>>(Object*&)  Wrong format. "
00965                             "Expecting \"*%d->\" but got \"*%d-%c\".", id, id, cc);
00966                 in.skipBlanksAndCommentsAndSeparators();
00967                 if(x)
00968                     in >> *x;
00969                 else // x is null
00970                     x = readObject(in, id);
00971                 //don't skip blanks before we need to read something else (read might block).
00972                 // in.skipBlanksAndCommentsAndSeparators();
00973                 in.copies_map_in[id]= x;
00974             } 
00975             else 
00976             {
00977                 // Find it in map and return ptr;
00978                 map<unsigned int, void *>::iterator it = in.copies_map_in.find(id);
00979                 if (it == in.copies_map_in.end())
00980                     PLERROR("In PStream::operator>>(Object*&) object (ptr) to be read with id='%d' "
00981                             "has not been previously defined", id);
00982                 x= static_cast<Object *>(it->second);
00983             }
00984         }
00985     }
00986     else
00987     {
00988         x = readObject(in);
00989         //don't skip blanks before we need to read something else (read might block).
00990         // in.skipBlanksAndCommentsAndSeparators();
00991     }
00992 
00993     return in;
00994 }
00995 
00996 void Object::remote_save(const string& filepath, const string& io_formatting) const
00997 {
00998     if(io_formatting=="plearn_ascii")
00999         PLearn::save(filepath, *this, PStream::plearn_ascii);
01000     else if(io_formatting=="plearn_binary")
01001         PLearn::save(filepath, *this, PStream::plearn_binary);
01002     else
01003         PLERROR("In Object remote method save: invalid io_formatting %s",
01004                 io_formatting.c_str());
01005 }
01006 
01007 void callFunction(const string& funcname, int nargs, PStream& io)
01008 {
01009     // Look up methodname in the RemoteMethodMap
01010     RemoteMethodMap& rmm = getGlobalFunctionMap();
01011     if (const RemoteTrampoline* trampoline = rmm.lookup(funcname,nargs))
01012         trampoline->call(0, nargs, io);
01013     else
01014         PLERROR("No function has been registered with name '%s' and %d arguments",
01015                 funcname.c_str(), nargs);
01016 }
01017 
01018 
01019 /*
01020   PStream& operator>>(PStream &in, Object * &o)
01021   {
01022   if (in.peek() == '*') {
01023   in.get(); // Eat '*'
01024   unsigned int id;
01025   in >> raw >> id;
01026   if (in.peek() == '-') {
01027   in.get(); // Eat '-'
01028   in.get(); // Eat '>'
01029             // Read object
01030             // NOTE: Object is added immediately to the map before any build() is called on it.
01031             o = readObject(in, id);
01032             } else {
01033             // Find it in map and return ptr
01034             map<unsigned int, void *>::iterator it = in.copies_map_in.find(id);
01035             if (it == in.copies_map_in.end())
01036             PLERROR("read() - Object to be read has not been previously defined");
01037             o = static_cast<Object *>(it->second);
01038             }
01039             } else {
01040             o = readObject(in);
01041             }
01042             return in;
01043             }
01044 */
01045 
01046 
01047 
01048 Object* newObjectFromClassname(const string& classname)
01049 {
01050     return TypeFactory::instance().newObject(classname);
01051 }
01052 
01053 Object* remote_deepCopy(Object* source)
01054 {
01055     CopiesMap copies;
01056     return source->deepCopy(copies);
01057 }
01058 
01059 BEGIN_DECLARE_REMOTE_FUNCTIONS
01060 
01061     declareFunction("newObject", &newObject,
01062                     (BodyDoc("Returns PLearn object from a string description.\n"),
01063                      ArgDoc("representation", 
01064                             "the string representation of the object"),
01065                      RetDoc ("newly created object")));
01066 
01067     declareFunction("newObjectFromClassname", &newObjectFromClassname,
01068                     (BodyDoc("Returns PLearn object from a class name (string.)\n"),
01069                      ArgDoc("classname", 
01070                             "the class of the object, as a string"),
01071                      RetDoc ("newly created object")));
01072 
01073     declareFunction("loadObject", &loadObject,
01074                     (BodyDoc("Returns PLearn object from a file describing it.\n"),
01075                      ArgDoc("filename", 
01076                             "file containing the object to load"),
01077                      RetDoc ("newly created object")));
01078 
01079     declareFunction("macroLoadObject", static_cast<Object* (*)(const PPath&,map<string,string>&)>(&macroLoadObject),
01080                     (BodyDoc("Returns PLearn object from a file describing it,"
01081                              " after macro-processing.\n"),
01082                      ArgDoc("filename", 
01083                             "file containing the object to load"),
01084                      ArgDoc("vars", 
01085                             "map of vars to values."),
01086                      RetDoc ("newly created object")));
01087 
01088     declareFunction("deepCopy", &remote_deepCopy,
01089                     (BodyDoc("Returns deep copy of a PLearn object.\n"),
01090                      ArgDoc ("source", "object to be deep-copied"),
01091                      RetDoc ("deep copy of the object")));
01092 
01093 
01094 END_DECLARE_REMOTE_FUNCTIONS
01095 
01096 
01097 } // end of namespace PLearn
01098 
01099 
01101 extern "C"
01102 void printobj(PLearn::Object* p)
01103 {
01104     PLearn::PStream perr(&std::cerr);
01105     perr << *p;
01106     perr.endl();
01107 }
01108 
01109 
01110 /*
01111   Local Variables:
01112   mode:c++
01113   c-basic-offset:4
01114   c-file-style:"stroustrup"
01115   c-file-offsets:((innamespace . 0)(inline-open . 0))
01116   indent-tabs-mode:nil
01117   fill-column:79
01118   End:
01119 */
01120 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines