00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "pytools/EnumType.h"
00017
00018
00019
00020
00021 #include <string>
00022 #include <cstring>
00023 #include <sstream>
00024
00025
00026
00027
00028
00029
00030
00031
00032 namespace {
00033
00034
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
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
00059
00060
00061
00062
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
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
00085 const char* p = strrchr( m_type.tp_name, '.' );
00086 std::string type = p ? p+1 : m_type.tp_name;
00087 type += '.';
00088
00089
00090 for (Enum* eiter = enums; eiter->name ; ++ eiter) {
00091
00092
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
00099 PyDict_SetItemString( m_type.tp_dict, eiter->name, (PyObject*)value );
00100
00101
00102 m_int2enum[eiter->value] = (PyObject*)value;
00103 }
00104
00105
00106 makeDocString();
00107 }
00108
00109
00110
00111
00112 pytools::EnumType::~EnumType ()
00113 {
00114
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
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
00148 PyType_Ready( &m_type );
00149 }
00150
00151
00152 void pytools::EnumType::makeDocString()
00153 {
00154
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
00161 items.insert(std::make_pair(std::string(PyString_AsString(pkey)), int(PyInt_AS_LONG(pvalue))));
00162 }
00163 }
00164
00165
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
00183
00184 void
00185 pytools::EnumType::addEnum(const std::string& name, int value)
00186 {
00187
00188 const char* p = strrchr( m_type.tp_name, '.' );
00189 std::string ename = p ? p+1 : m_type.tp_name;
00190 ename += '.';
00191
00192
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
00199 PyDict_SetItemString(m_type.tp_dict, name.c_str(), (PyObject*)evalue);
00200
00201
00202 m_int2enum[value] = (PyObject*)evalue;
00203
00204
00205 makeDocString();
00206 }
00207
00208
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
00225 if ( not m_type.tp_dict ) {
00226 PyErr_SetString( PyExc_TypeError, "Type has no dictionary." );
00227 return 0;
00228 }
00229
00230
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
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
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
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
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
00317 Py_CLEAR( ((EnumObject*)self)->en_name);
00318
00319
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 }