pytools/src/EnumType.cpp

Go to the documentation of this file.
00001 //--------------------------------------------------------------------------
00002 // File and Version Information:
00003 //      $Id: EnumType.cpp 5266 2013-01-31 20:14:36Z salnikov@SLAC.STANFORD.EDU $
00004 //
00005 // Description:
00006 //      Class EnumType...
00007 //
00008 // Author List:
00009 //      Andrei Salnikov
00010 //
00011 //------------------------------------------------------------------------
00012 
00013 //-----------------------
00014 // This Class's Header --
00015 //-----------------------
00016 #include "pytools/EnumType.h"
00017 
00018 //-----------------
00019 // C/C++ Headers --
00020 //-----------------
00021 #include <string>
00022 #include <cstring>
00023 #include <sstream>
00024 
00025 //-------------------------------
00026 // Collaborating Class Headers --
00027 //-------------------------------
00028 
00029 //-----------------------------------------------------------------------
00030 // Local Macros, Typedefs, Structures, Unions and Forward Declarations --
00031 //-----------------------------------------------------------------------
00032 namespace {
00033 
00034   // enum object is an integer with attached name
00035   struct EnumObject : PyIntObject {
00036     PyObject* en_name ;
00037   };
00038 
00039   PyObject initHeader = { PyObject_HEAD_INIT(0) };
00040 
00041   char docString[] =
00042     "Python class which emulates C++ enumeration. It defines class attributes\n"
00043     "whose values are integer numbers, so that you can use EnumType.EnumValue\n"
00044     "notation to access the values. Instances of this class are integer numbers\n"
00045     "which when converted to string give the name of the corresponding class\n"
00046     "attribute, e.g. str(Enum.SomeConst) => \"Enum.SomeConst\" (but \n"
00047     "repr(Enum.SomeConst) => 42)\n";
00048 
00049   // standard Python stuff
00050   void Enum_dealloc( PyObject* self );
00051   PyObject* Enum_str_repr( PyObject* self );
00052   PyObject* Enum_str_int( PyObject* self );
00053   int Enum_init(PyObject* self, PyObject* args, PyObject* kwds);
00054 
00055 }
00056 
00057 //              ----------------------------------------
00058 //              -- Public Function Member Definitions --
00059 //              ----------------------------------------
00060 
00061 //----------------
00062 // Constructors --
00063 //----------------
00064 pytools::EnumType::EnumType(const char* typeName)
00065   : m_typeName(0)
00066   , m_type()
00067   , m_int2enum()
00068   , m_docString(0)
00069 {
00070   initType(typeName);
00071 
00072   // generate doc string
00073   makeDocString();
00074 }
00075 
00076 pytools::EnumType::EnumType(const char* typeName, Enum* enums)
00077   : m_typeName(0)
00078   , m_type()
00079   , m_int2enum()
00080   , m_docString(0)
00081 {
00082   initType(typeName);
00083 
00084   // this enum class name
00085   const char* p = strrchr( m_type.tp_name, '.' );
00086   std::string type = p ? p+1 : m_type.tp_name;
00087   type += '.';
00088 
00089   // define class attributes as enum values
00090   for (Enum* eiter = enums; eiter->name ; ++ eiter) {
00091 
00092     // build the object
00093     EnumObject* value = PyObject_New(EnumObject, &m_type);
00094     std::string name = type + eiter->name;
00095     value->ob_ival = eiter->value;
00096     value->en_name = PyString_FromString(name.c_str());
00097 
00098     // store it in the class attribute
00099     PyDict_SetItemString( m_type.tp_dict, eiter->name, (PyObject*)value );
00100 
00101     // and in the map
00102     m_int2enum[eiter->value] = (PyObject*)value;
00103   }
00104 
00105   // generate doc string
00106   makeDocString();
00107 }
00108 
00109 //--------------
00110 // Destructor --
00111 //--------------
00112 pytools::EnumType::~EnumType ()
00113 {
00114   // "free" all referenced objects
00115   for ( Int2Enum::iterator it = m_int2enum.begin() ; it != m_int2enum.begin() ; ++ it ) {
00116     Py_CLEAR( it->second );
00117   }
00118 }
00119 
00120 void
00121 pytools::EnumType::initType(const char* typeName)
00122 {
00123   size_t len = std::strlen(typeName);
00124   m_typeName = new char[len + 1];
00125   std::strcpy(m_typeName, typeName);
00126 
00127   // initialize type structure
00128   memset ( &m_type, 0, sizeof m_type );
00129   memcpy( &m_type, &initHeader, sizeof initHeader );
00130   m_type.tp_name = m_typeName;
00131   m_type.tp_basicsize = sizeof(EnumObject);
00132   m_type.tp_dealloc = Enum_dealloc;
00133   m_type.tp_repr = Enum_str_int;
00134   m_type.tp_str = Enum_str_repr;
00135   m_type.tp_flags = Py_TPFLAGS_DEFAULT;
00136   m_type.tp_doc = 0;
00137   m_type.tp_base = &PyInt_Type ;
00138   m_type.tp_init = Enum_init ;
00139   m_type.tp_alloc = PyType_GenericAlloc ;
00140   m_type.tp_new = PyType_GenericNew ;
00141   m_type.tp_free = _PyObject_Del ;
00142   m_type.tp_del = Enum_dealloc;
00143 
00144   PyObject* tp_dict = PyDict_New();
00145   m_type.tp_dict = tp_dict;
00146 
00147   // finalize type
00148   PyType_Ready( &m_type );
00149 }
00150 
00151 
00152 void pytools::EnumType::makeDocString()
00153 {
00154   // get sorted set of keys and values
00155   std::map<std::string, int> items;
00156   Py_ssize_t ppos = 0;
00157   PyObject *pkey, *pvalue;
00158   while (PyDict_Next(m_type.tp_dict, &ppos, &pkey, &pvalue)) {
00159     if (pvalue->ob_type == &m_type) {
00160       // pkey must be a string, what else could it be?
00161       items.insert(std::make_pair(std::string(PyString_AsString(pkey)), int(PyInt_AS_LONG(pvalue))));
00162     }
00163   }
00164 
00165   // generate doc string
00166   std::ostringstream doc;
00167   doc << ::docString << "\nThis class defines following constants:";
00168   for (std::map<std::string, int>::const_iterator it = items.begin(); it != items.end(); ++ it) {
00169     doc << " " << it->first << "=" << it->second;
00170   }
00171 
00172   const std::string& docstr = doc.str();
00173 
00174   if (m_docString) delete [] m_docString;
00175   m_docString = new char[docstr.size()+1];
00176   std::copy(docstr.begin(), docstr.end(), m_docString);
00177   m_docString[docstr.size()] = '\0';
00178   m_type.tp_doc = m_docString;
00179 }
00180 
00181 /**
00182  *  Add one more enum value to the type.
00183  */
00184 void
00185 pytools::EnumType::addEnum(const std::string& name, int value)
00186 {
00187   // this enum class name
00188   const char* p = strrchr( m_type.tp_name, '.' );
00189   std::string ename = p ? p+1 : m_type.tp_name;
00190   ename += '.';
00191 
00192   // build the object
00193   EnumObject* evalue = PyObject_New(EnumObject, &m_type);
00194   ename += name;
00195   evalue->ob_ival = value;
00196   evalue->en_name = PyString_FromString(ename.c_str());
00197 
00198   // store it as the class attribute
00199   PyDict_SetItemString(m_type.tp_dict, name.c_str(), (PyObject*)evalue);
00200 
00201   // and in the map
00202   m_int2enum[value] = (PyObject*)evalue;
00203 
00204   // update doc string
00205   makeDocString();
00206 }
00207 
00208 // Make instance of this type
00209 PyObject*
00210 pytools::EnumType::Enum_FromLong( long value ) const
00211 {
00212   Int2Enum::const_iterator it = m_int2enum.find( value );
00213   if ( it == m_int2enum.end() ) {
00214     return PyInt_FromLong(value);
00215   }
00216 
00217   Py_INCREF( it->second );
00218   return it->second;
00219 }
00220 
00221 PyObject*
00222 pytools::EnumType::Enum_FromString( const char* name ) const
00223 {
00224   // try to find a name in the class attributes
00225   if ( not m_type.tp_dict ) {
00226     PyErr_SetString( PyExc_TypeError, "Type has no dictionary." );
00227     return 0;
00228   }
00229 
00230   // get the class attribute with this name
00231   PyObject* val = PyDict_GetItemString( m_type.tp_dict, name );
00232 
00233   if ( not val ) {
00234     PyErr_Format( PyExc_TypeError, "Unknown enum name (%s)", name );
00235     return 0;
00236   }
00237 
00238   // return the object
00239   Py_INCREF( val );
00240   return val;
00241 }
00242 
00243 namespace {
00244 
00245 int
00246 Enum_init(PyObject* self, PyObject* args, PyObject* kwds)
00247 {
00248   EnumObject* py_this = (EnumObject*) self;
00249   if ( not py_this ) {
00250     PyErr_SetString(PyExc_RuntimeError, "Error: self is NULL");
00251     return -1;
00252   }
00253   py_this->ob_ival = 0;
00254   py_this->en_name = 0;
00255 
00256   // expect integer or string
00257   PyObject* arg;
00258   if ( not PyArg_ParseTuple( args, "O:Enum_Init", &arg ) ) {
00259     return -1 ;
00260   }
00261 
00262   if ( PyInt_Check(arg) ) {
00263 
00264     // dirty hack
00265     pytools::EnumType* enumType = (pytools::EnumType*)self->ob_type;
00266     if ( PyObject* o = enumType->Enum_FromLong( PyInt_AsLong(arg) ) ) {
00267 
00268       if ( PyObject_TypeCheck( o, self->ob_type ) ) {
00269         EnumObject* enumObj = (EnumObject*)o;
00270         py_this->ob_ival = enumObj->ob_ival;
00271         py_this->en_name = enumObj->en_name;
00272         Py_INCREF(py_this->en_name);
00273       } else {
00274         PyIntObject* enumObj = (PyIntObject*)o;
00275         py_this->ob_ival = enumObj->ob_ival;
00276         py_this->en_name = Py_None;
00277         Py_INCREF(py_this->en_name);
00278       }
00279 
00280     } else {
00281 
00282       return -1;
00283 
00284     }
00285 
00286   } else if ( PyString_Check(arg) ) {
00287 
00288     // dirty hack
00289     pytools::EnumType* enumType = (pytools::EnumType*)self->ob_type;
00290     if ( PyObject* o = enumType->Enum_FromString( PyString_AsString(arg) ) ) {
00291 
00292       EnumObject* enumObj = (EnumObject*)o;
00293       py_this->ob_ival = enumObj->ob_ival;
00294       py_this->en_name = enumObj->en_name;
00295       Py_INCREF(py_this->en_name);
00296 
00297     } else {
00298 
00299       return -1;
00300 
00301     }
00302 
00303   } else {
00304 
00305     PyErr_SetString(PyExc_RuntimeError, "Error: unknown argument type");
00306     return -1;
00307 
00308   }
00309 
00310   return 0;
00311 }
00312 
00313 void
00314 Enum_dealloc( PyObject* self )
00315 {
00316   // free the name
00317   Py_CLEAR( ((EnumObject*)self)->en_name);
00318 
00319   // deallocate ourself
00320   self->ob_type->tp_free(self);
00321 }
00322 
00323 PyObject*
00324 Enum_str_repr( PyObject* self )
00325 {
00326   PyObject* name = ((EnumObject*) self)->en_name;
00327   Py_INCREF(name) ;
00328   return name;
00329 }
00330 
00331 PyObject*
00332 Enum_str_int( PyObject* self )
00333 {
00334   long val = ((PyIntObject*) self)->ob_ival;
00335   char buf[64];
00336   snprintf(buf, sizeof buf, "%ld", val);
00337   return PyString_FromString(buf);
00338 }
00339 
00340 }

Generated on 19 Dec 2016 for PSDMSoftware by  doxygen 1.4.7