PLearn 0.1
PythonExtension.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 // PythonExtension.h
00004 // Copyright (C) 2007 Xavier Saint-Mleux, ApSTAT Technologies inc.
00005 
00006 // Redistribution and use in source and binary forms, with or without
00007 // modification, are permitted provided that the following conditions are met:
00008 // 
00009 //  1. Redistributions of source code must retain the above copyright
00010 //     notice, this list of conditions and the following disclaimer.
00011 // 
00012 //  2. Redistributions in binary form must reproduce the above copyright
00013 //     notice, this list of conditions and the following disclaimer in the
00014 //     documentation and/or other materials provided with the distribution.
00015 // 
00016 //  3. The name of the authors may not be used to endorse or promote
00017 //     products derived from this software without specific prior written
00018 //     permission.
00019 // 
00020 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
00021 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00022 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
00023 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
00024 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
00025 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00026 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00027 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00028 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00029 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 // 
00031 // This file is part of the PLearn library. For more information on the PLearn
00032 // library, go to the PLearn Web site at www.plearn.org
00033 
00034 #define PL_LOG_MODULE_NAME "PythonExtension"
00035 
00036 #include <plearn/python/PythonExtension.h>
00037 #include <plearn/python/PythonObjectWrapper.h>
00038 #include <plearn/base/RemoteDeclareMethod.h>
00039 #include <plearn/base/HelpSystem.h>
00040 #include <plearn/base/TypeFactory.h>
00041 #include <plearn/base/PMemPool.h>
00042 #include <plearn/base/stringutils.h>
00043 #include <plearn/vmat/VMatrix.h>
00044 #include <plearn/io/pl_log.h>
00045 #include <plearn/sys/procinfo.h>
00046 
00047 namespace PLearn {
00048 
00049 // Trampoline for global PLearn 'remote' functions
00050 PyObject* pythonGlobalFuncTramp(PyObject* self, PyObject* args)
00051 {
00052     DBG_MODULE_LOG << "pythonGlobalFuncTramp(" << PythonObjectWrapper(self)
00053                    << ", " << PythonObjectWrapper(args) << ')' << endl;
00054     RemoteTrampoline* t= 
00055         static_cast<RemoteTrampoline*>(PyCObject_AsVoidPtr(self));
00056     int nas= PyTuple_GET_SIZE(args);
00057     TVec<PythonObjectWrapper> as;
00058     for(int i= 0; i < nas; ++i)
00059         as.push_back(PythonObjectWrapper(PyTuple_GET_ITEM(args, i)));
00060 
00061     PythonObjectWrapper::gc_collect1();
00062 
00063     try
00064     {
00065         PythonObjectWrapper returned_value= t->call(0, as);
00066         PyObject* to_return= returned_value.getPyObject();
00067         Py_XINCREF(to_return);
00068         return to_return;
00069     }
00070     catch(const PLearnError& e) 
00071     {
00072         PyErr_SetString(the_PLearn_python_exception, e.message().c_str());
00073         return 0;
00074     }
00075     catch(const std::exception& e) 
00076     {
00077         PyErr_SetString(PyExc_Exception, e.what());
00078         return 0;
00079     }
00080     catch(...) 
00081     {
00082         PyErr_SetString(PyExc_Exception,
00083                         "Caught unknown C++ exception");
00084         return 0;
00085     }
00086 }
00087 
00088 // the global funcs (storage never reclaimed)
00089 static PObjectPool<PyMethodDef> pyfuncs(50);
00090 static TVec<string> funcs_help;
00091 
00092 void injectPLearnGlobalFunctions(PyObject* env)
00093 {
00094     const RemoteMethodMap::MethodMap& global_funcs= 
00095         getGlobalFunctionMap().getMap();
00096 
00097     for(RemoteMethodMap::MethodMap::const_iterator it=
00098             global_funcs.begin();
00099         it != global_funcs.end(); ++it)
00100     {
00101         PyObject* self= 
00102             PyCObject_FromVoidPtr(it->second, NULL);
00103     
00104         PyMethodDef* py_method= pyfuncs.allocate();
00105         py_method->ml_name= 
00106             const_cast<char*>(it->first.first.c_str());
00107         py_method->ml_meth= pythonGlobalFuncTramp;
00108         py_method->ml_flags= METH_VARARGS;
00109         funcs_help.push_back(
00110             HelpSystem::helpOnFunction(it->first.first.c_str(), 
00111                                        it->first.second));
00112         py_method->ml_doc= const_cast<char*>(funcs_help.last().c_str());
00113     
00114         /* module= env if env is a module; NULL otherwise */
00115         PyObject* module= 0;
00116         if(PyModule_Check(env))
00117             module= env;
00118 
00119         // N.B.: module == NULL works on python2.3, 2.4 and 2.5, but is not
00120         // documented
00121         PyObject* pyfunc= 
00122             PyCFunction_NewEx(py_method, self, module);
00123             
00124         if(pyfunc) 
00125             PyObject_SetAttrString(env, 
00126                                    py_method->ml_name, 
00127                                    pyfunc);
00128         else
00129             PLERROR("Cannot inject global function "
00130                     "'%s' into python.",
00131                     py_method->ml_name);
00132     }
00133 }
00134 
00135 
00136 void injectPLearnClasses(PyObject* module)
00137 {
00138     PythonGlobalInterpreterLock gil;         // For thread-safety
00139     PythonObjectWrapper::initializePython();
00140 
00141     if(!PyModule_Check(module))
00142         PLERROR("In injectPLearnClasses : "
00143                 "module param. should be a python module.");
00144     // import python class for wrapping PLearn objects
00145     string importcode= "\nfrom plearn.pybridge.wrapped_plearn_object "
00146         "import *\n";
00147     PyObject_SetAttrString(module, const_cast<char*>("__builtins__"), PyEval_GetBuiltins());
00148     PyObject* res= PyRun_String(importcode.c_str(), Py_file_input, 
00149                                 PyModule_GetDict(module), PyModule_GetDict(module));
00150     if(!res)
00151     {
00152         Py_DECREF(module);
00153         if(PyErr_Occurred()) PyErr_Print();
00154         PLERROR("in injectPLearnClasses : cannot import plearn.pybridge.wrapped_plearn_object.");
00155     }
00156     Py_DECREF(res);
00157 
00158     string wrapper_name= "WrappedPLearnObject";
00159     // now find the class in the env.
00160     typedef map<string, PyObject*> env_t;
00161     env_t env= PythonObjectWrapper(
00162         PyModule_GetDict(module), 
00163         PythonObjectWrapper::transfer_ownership).as<env_t>();
00164     env_t::iterator clit= env.find(wrapper_name);
00165     if(clit == env.end())
00166         PLERROR("in injectPLearnClasses : "
00167                 "class %s not defined "
00168                 "in plearn.pybridge.wrapped_plearn_object",
00169                 wrapper_name.c_str());
00170     PyObject* wrapper= clit->second;
00171 
00172     //inject unref and newCPPObj methods
00173     PyMethodDef* py_method= &PythonObjectWrapper::m_unref_method_def;
00174     py_method->ml_name  = const_cast<char*>("_unref");
00175     py_method->ml_meth  = PythonObjectWrapper::python_del;
00176     py_method->ml_flags = METH_VARARGS;
00177     py_method->ml_doc   = const_cast<char*>("Injected unref function from PythonObjectWrapper; "
00178                                             "DO NOT USE THIS FUNCTION! IT MAY DEALLOCATE THE PLEARN OBJECT!");
00179     PyObject* py_funcobj= PyCFunction_NewEx(py_method, NULL, NULL);
00180     PyObject* py_methobj= PyMethod_New(py_funcobj, NULL, wrapper);
00181     Py_XDECREF(py_funcobj);
00182     if(!py_funcobj || !py_methobj) 
00183     {
00184         Py_DECREF(module);
00185         Py_XDECREF(py_methobj);
00186         PLERROR("in injectPLearnClasses : "
00187                 "can't inject method '%s' (i.e. __del__)", 
00188                 py_method->ml_name);
00189     }
00190     PyObject_SetAttrString(wrapper, py_method->ml_name, py_methobj);
00191     Py_DECREF(py_methobj);
00192 
00193     // inject 'newCPPObj' and 'refCPPObj' class methods
00194     TVec<PyMethodDef*> classmethods(2);
00195     classmethods[0]= &PythonObjectWrapper::m_newCPPObj_method_def;
00196     classmethods[0]->ml_name  = const_cast<char*>("_newCPPObj");
00197     classmethods[0]->ml_meth  = PythonObjectWrapper::newCPPObj;
00198     classmethods[0]->ml_flags = METH_VARARGS;
00199     classmethods[0]->ml_doc   = const_cast<char*>("Injected new function from PythonObjectWrapper; "
00200                                                   "DO NOT USE THIS FUNCTION!");
00201     classmethods[1]= &PythonObjectWrapper::m_refCPPObj_method_def;
00202     classmethods[1]->ml_name  = const_cast<char*>("_refCPPObj");
00203     classmethods[1]->ml_meth  = PythonObjectWrapper::refCPPObj;
00204     classmethods[1]->ml_flags = METH_VARARGS;
00205     classmethods[1]->ml_doc   = const_cast<char*>("Injected new function from PythonObjectWrapper; "
00206                                                   "DO NOT USE THIS FUNCTION!");
00207 
00208     for(TVec<PyMethodDef*>::iterator mit= classmethods.begin();
00209         mit != classmethods.end(); ++mit)
00210     {
00211         py_method= *mit;
00212         py_funcobj= PyCFunction_NewEx(py_method, NULL, NULL);
00213         py_methobj= PyMethod_New(py_funcobj, NULL, wrapper);
00214         Py_XDECREF(py_funcobj);
00215         if(!py_funcobj || !py_methobj) 
00216         {
00217             Py_DECREF(module);
00218             Py_XDECREF(py_methobj);
00219             PLERROR("in injectPLearnClasses : "
00220                     "can't inject method '%s' (i.e. C++'s new)", 
00221                     py_method->ml_name);
00222         }
00223         PyObject_SetAttrString(wrapper, py_method->ml_name, py_methobj);
00224         Py_DECREF(py_methobj);
00225         
00226         string classmethodname= wrapper_name+"."+py_method->ml_name;
00227         res= PyRun_String((classmethodname
00228                            +"= classmethod("+classmethodname+".im_func)").c_str(), 
00229                           Py_file_input, 
00230                           PyModule_GetDict(module), PyModule_GetDict(module));
00231         Py_DECREF(res);
00232         if (PyErr_Occurred()) 
00233         {
00234             Py_DECREF(module);
00235             PyErr_Print();
00236             PLERROR("in injectPLearnClasses : error making "
00237                     "newCPPObj a classmethod.");
00238         }
00239     }
00240 
00241     if(0 != PyType_Ready(reinterpret_cast<PyTypeObject*>(wrapper)))
00242         PLERROR("in injectPLearnClasses : "
00243                 "failed PyType_Ready on wrapper class.");
00244 
00245     if(!PyCallable_Check(wrapper))
00246         PLERROR("in injectPLearnClasses : "
00247                 "%s is not callable [not a class?]",
00248                 wrapper_name.c_str());
00249 
00250     PyObject* the_pyclass= 0;
00251     const TypeMap& tp_map= TypeFactory::instance().getTypeMap();
00252     for(TypeMap::const_iterator tit= tp_map.begin();
00253         tit != tp_map.end(); ++tit)
00254     {
00255         if(!tit->second.constructor)
00256             continue; //skip abstract classes
00257 
00258         string actual_wrapper_name= wrapper_name;
00259         Object* temp_obj= tit->second.constructor();
00260         // use different wrapper for VMats (w/ len, getitem, etc.)
00261         if(dynamic_cast<VMatrix*>(temp_obj))
00262             actual_wrapper_name= "WrappedPLearnVMat";
00263         delete temp_obj;
00264 
00265         // create new python type deriving from WrappedPLearnObject
00266         string classname= tit->first;
00267         string class_help_text= HelpSystem::helpOnClass(classname);
00268         search_replace(class_help_text, "\"\"\"", "\\\"\\\"\\\"");
00269         string pyclassname= classname;
00270         search_replace(pyclassname, " ", "_");
00271         search_replace(pyclassname, "<", "_");
00272         search_replace(pyclassname, ">", "_");
00273         string derivcode= string("\nclass ")
00274             + pyclassname + "(" + actual_wrapper_name + "):\n"
00275             "  \"\"\" \n" + class_help_text + "\n \"\"\"\n"
00276             "  def __new__(cls,*args,**kwargs):\n"
00277             "    #get_plearn_module().loggingControl(500, ['__ALL__'])"
00278             "    #print '** "+pyclassname+".__new__',args,kwargs\n"
00279             "    #import sys; sys.stdout.flush()\n"
00280             "    obj= object.__new__(cls)\n"
00281             "    if '_cptr' not in kwargs:\n"
00282             "      obj._cptr= cls._newCPPObj('"+classname+"')\n"
00283             "      cls._refCPPObj(obj)\n"
00284             "    return obj\n";
00285         PyObject* res= PyRun_String(derivcode.c_str(), 
00286                                     Py_file_input, 
00287                                     PyModule_GetDict(module), PyModule_GetDict(module));
00288 
00289         Py_XDECREF(res);
00290         env= PythonObjectWrapper(
00291             PyModule_GetDict(module), 
00292             PythonObjectWrapper::transfer_ownership).as<env_t>();
00293         clit= env.find(pyclassname);
00294         if(clit == env.end())
00295             PLERROR("in injectPLearnClasses : "
00296                     "Cannot create new python class deriving from "
00297                     "%s (%s).", 
00298                     actual_wrapper_name.c_str(),
00299                     classname.c_str());
00300 
00301         //set option names
00302         OptionList& options= tit->second.getoptionlist_method();
00303         unsigned int nopts= options.size();
00304         //set<string> optionnames;
00305         map<string, vector<string> > optionnames;
00306         for(unsigned int i= 0; i < nopts; ++i)
00307             //optionnames.insert(options[i]->optionname());
00308             optionnames[options[i]->optionname()]= options[i]->flagStrings();
00309         the_pyclass= clit->second;
00310         if(-1==PyObject_SetAttrString(the_pyclass, const_cast<char*>("_optionnames"), 
00311                                       PythonObjectWrapper(optionnames).getPyObject()))
00312         {
00313             Py_DECREF(module);
00314             if (PyErr_Occurred()) PyErr_Print();
00315             PLERROR("in injectPLearnClasses : "
00316                     "cannot set attr _optionnames for class %s",
00317                     classname.c_str());
00318         }
00319 
00320         // inject all declared methods
00321         const RemoteMethodMap* methods= &tit->second.get_remote_methods();
00322 
00323         PP<PObjectPool<PyMethodDef> > meth_def_pool= 
00324             new PObjectPool<PyMethodDef>(methods->size()+1);
00325 
00326         PythonObjectWrapper::m_pypl_classes.insert(
00327             make_pair(classname, PLPyClass(the_pyclass, meth_def_pool)));
00328         TVec<string>& methods_help= 
00329             PythonObjectWrapper::m_pypl_classes.find(classname)->second.methods_help;
00330 
00331         set<pair<string, int> > methods_done;
00332         while(methods)
00333         {
00334             for(RemoteMethodMap::MethodMap::const_iterator it= methods->begin();
00335                 it != methods->end(); ++it)
00336             {
00337                 //skip methods already injected, or not to inject
00338                 if(methods_done.find(it->first) != methods_done.end()) continue;
00339                 methods_done.insert(it->first);
00340                 if(it->second->flags() & RemoteTrampoline::nopython) continue;
00341 
00342                 //get the RemoteTrampoline
00343                 PyObject* tramp= PyCObject_FromVoidPtr(it->second, NULL);
00344             
00345                 // Create a Python Function Object
00346                 PyMethodDef* py_method= meth_def_pool->allocate();
00347                 py_method->ml_name  = const_cast<char*>(it->first.first.c_str());
00348                 py_method->ml_meth  = PythonObjectWrapper::trampoline;
00349                 py_method->ml_flags = METH_VARARGS;
00350                 methods_help.push_back(HelpSystem::helpOnMethod(classname,
00351                                                                 it->first.first.c_str(),
00352                                                                 it->first.second));
00353                 py_method->ml_doc   = const_cast<char*>(methods_help.last().c_str());
00354     
00355                 PyObject* py_funcobj= PyCFunction_NewEx(py_method, tramp, module);
00356 
00357                 // create an unbound method from the function
00358                 PyObject* py_methobj= PyMethod_New(py_funcobj, NULL, the_pyclass);
00359 
00360                 Py_DECREF(tramp);
00361                 Py_XDECREF(py_funcobj);
00362                 if(!py_funcobj || !py_methobj) 
00363                 {
00364                     Py_DECREF(module);
00365                     Py_XDECREF(py_methobj);
00366                     PLERROR("in injectPLearnClasses : "
00367                             "can't inject method '%s'", py_method->ml_name);
00368                 }
00369 
00370                 if(-1==PyObject_SetAttrString(the_pyclass, py_method->ml_name, py_methobj))
00371                 {
00372                     Py_DECREF(py_methobj);
00373                     if (PyErr_Occurred()) PyErr_Print();
00374                     PLERROR("in injectPLearnClasses : "
00375                             "cannot set attr %s for class %s",
00376                             py_method->ml_name,
00377                             classname.c_str());
00378                 }
00379                 Py_DECREF(py_methobj);
00380             }
00381             methods= methods->inheritedMethods();//get parent class methods
00382         }
00383         if(0 != PyType_Ready(reinterpret_cast<PyTypeObject*>(the_pyclass)))
00384             PLERROR("in injectPLearnClasses : "
00385                     "failed PyType_Ready on class %s.",classname.c_str());
00386         if(module)
00387             if(-1==PyObject_SetAttrString(module, 
00388                                           const_cast<char*>(pyclassname.c_str()), 
00389                                           the_pyclass))
00390             {
00391                 if (PyErr_Occurred()) PyErr_Print();
00392                 PLERROR("in injectPLearnClasses : "
00393                         "cannot inject class %s.",
00394                         classname.c_str());
00395             }
00396     }
00397 }
00398 
00399 PyObject* the_PLearn_python_exception= 0;
00400 void injectPLearnException(PyObject* module)
00401 {
00402     PyObject* res= PyRun_String("class PLearnError(Exception):\n\tpass\n", 
00403                                 Py_file_input, 
00404                                 PyModule_GetDict(module), 
00405                                 PyModule_GetDict(module));
00406     if(!res)
00407     {
00408         if(PyErr_Occurred()) PyErr_Print();
00409         PLERROR("in injectPLearnException : cannot create PLearnException class.");
00410     }
00411     Py_DECREF(res);
00412 
00413     the_PLearn_python_exception= PyObject_GetAttrString(module, "PLearnError");
00414     if(!the_PLearn_python_exception)
00415     {
00416         if(PyErr_Occurred()) PyErr_Print();
00417         PLERROR("in injectPLearnException : cannot retrieve PLearnException class.");
00418     }
00419     //keep ref. to PLearnError forever.
00420 }
00421 
00422 
00423 void createWrappedObjectsSet(PyObject* module)
00424 {
00425     /* can't set logging before this gets called
00426     perr << "[pid=" << getPid() << "] "
00427          << "createWrappedObjectsSet for module: " << PythonObjectWrapper(module) << endl;
00428     */
00429 
00430     string code= "";
00431 #if PL_PYTHON_VERSION <= 230
00432     code+= "\nfrom sets import Set as set\n";
00433 #endif // PL_PYTHON_VERSION <= 230
00434     code+= "\nwrapped_PLearn_instances= set()\n";
00435     PyObject* res= PyRun_String(code.c_str(), Py_file_input, 
00436                                 PyModule_GetDict(module), PyModule_GetDict(module));
00437     if(!res)
00438     {
00439         if(PyErr_Occurred()) PyErr_Print();
00440         PLERROR("in createWrappedObjectsSet : cannot create set.");
00441     }
00442     Py_DECREF(res);
00443  }
00444 
00445 void addToWrappedObjectsSet(PyObject* o)
00446 {
00447     DBG_MODULE_LOG << "addToWrappedObjectsSet for module: " << PythonObjectWrapper(the_PLearn_python_module)
00448                    << "\tadding object: " << PythonObjectWrapper(o) << endl;
00449     PLASSERT(the_PLearn_python_module);
00450     if(-1 == PyObject_SetAttrString(the_PLearn_python_module, const_cast<char*>("_tmp_wrapped_instance"), o))
00451         PLERROR("in addToWrappedObjectsSet : cannot add wrapped object to module.");
00452     PyObject* res= PyRun_String("\nwrapped_PLearn_instances.add(_tmp_wrapped_instance)"
00453                                 "\ndel _tmp_wrapped_instance\n", 
00454                                 Py_file_input, 
00455                                 PyModule_GetDict(the_PLearn_python_module), 
00456                                 PyModule_GetDict(the_PLearn_python_module));
00457     if(!res)
00458     {
00459         if(PyErr_Occurred()) PyErr_Print();
00460         PLERROR("in addToWrappedObjectsSet : cannot add wrapped object to set.");
00461     }
00462     Py_DECREF(res);
00463 }
00464 void removeFromWrappedObjectsSet(PyObject* o)
00465 {
00466     DBG_MODULE_LOG << "removeFromWrappedObjectsSet for module: " << PythonObjectWrapper(the_PLearn_python_module)
00467                    << "\tremoving object: " << PythonObjectWrapper(o) << endl;
00468     PLASSERT(the_PLearn_python_module);
00469     if(-1 == PyObject_SetAttrString(the_PLearn_python_module, const_cast<char*>("_tmp_wrapped_instance"), o))
00470         PLERROR("in removeFromWrappedObjectsSet : cannot add wrapped object to module.");
00471     PyObject* res= PyRun_String("\nwrapped_PLearn_instances.remove(_tmp_wrapped_instance)"
00472                                 "\ndel _tmp_wrapped_instance\n", 
00473                                 Py_file_input, 
00474                                 PyModule_GetDict(the_PLearn_python_module), 
00475                                 PyModule_GetDict(the_PLearn_python_module));
00476     if(!res)
00477     {
00478         if(PyErr_Occurred()) PyErr_Print();
00479         PLERROR("in removeFromWrappedObjectsSet : cannot remove wrapped object from set.");
00480     }
00481     Py_DECREF(res);
00482 }
00483 
00484 // Init func for python module.
00485 // init module, then inject global funcs
00486 void initPythonExtensionModule(char const * module_name)
00487 {
00488     /* // can't set logging before this gets called
00489     perr << "[pid=" << getPid() << "] "
00490          << "initPythonExtensionModule name=" << module_name << endl;
00491     */
00492     PythonObjectWrapper::initializePython();
00493     PyObject* plext= Py_InitModule(const_cast<char*>(module_name), NULL);
00494     setPythonModuleAndInject(plext);
00495 }
00496 
00497 void setPythonModuleAndInject(PyObject* module)
00498 {
00499     /* //can't set logging before this gets called
00500     perr << "[pid=" << getPid() << "] "
00501          << "setPythonModuleAndInject for module: " << PythonObjectWrapper(module) << "\tat " << (void*)module << endl;
00502     */
00503     injectPLearnGlobalFunctions(module);
00504     injectPLearnClasses(module);
00505     injectPLearnException(module);
00506     createWrappedObjectsSet(module);
00507     the_PLearn_python_module= module;   
00508 }
00509 
00510 PyObject* the_PLearn_python_module= 0;
00511 
00512 
00513 
00514 
00515 void setNullPout()
00516 {
00517     pout= pnull;
00518 }
00519 void setPoutToPerr()
00520 {
00521     pout= perr;
00522 }
00523 
00524 BEGIN_DECLARE_REMOTE_FUNCTIONS
00525 
00526     declareFunction("setNullPout", &setNullPout,
00527                     (BodyDoc("Sets the pout output stream to be null.\n")));
00528 
00529     declareFunction("setPoutToPerr", &setPoutToPerr,
00530                     (BodyDoc("Sets the pout output stream to be perr.\n")));
00531 
00532 END_DECLARE_REMOTE_FUNCTIONS
00533         
00534 
00535 
00536 
00537 } // end of namespace PLearn
00538 
00539 
00540 /*
00541   Local Variables:
00542   mode:c++
00543   c-basic-offset:4
00544   c-file-style:"stroustrup"
00545   c-file-offsets:((innamespace . 0)(inline-open . 0))
00546   indent-tabs-mode:nil
00547   fill-column:79
00548   End:
00549 */
00550 // 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