PLearn 0.1
|
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>&)>(¯oLoadObject), 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 :