/*==LICENSE==* CyanWorlds.com Engine - MMOG client, server and tools Copyright (C) 2011 Cyan Worlds, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . You can contact Cyan Worlds, Inc. by email legal@cyan.com or by snail mail at: Cyan Worlds, Inc. 14617 N Newport Hwy Mead, WA 99021 *==LICENSE==*/ ///////////////////////////////////////////////////////////////////////////// // // NAME: pyEnum // // PURPOSE: Base class stuff for enumeration support (you don't instance this // class // #include "pyEnum.h" #include #include "structmember.h" #include "pyGlueHelpers.h" #if HS_BUILD_FOR_MAC #include #include #endif struct EnumValue { PyObject_HEAD long value; char* name; }; PyObject *NewEnumValue(char const* name, long value); static PyObject *EnumValue_new(PyTypeObject *type, PyObject *args, PyObject *) { EnumValue *self = (EnumValue*)type->tp_alloc(type, 0); if (self != NULL) { char *nameTemp = NULL; long value; if (!PyArg_ParseTuple(args, "l|s", &value, &nameTemp)) { Py_DECREF(self); return NULL; } if (nameTemp) // copy the value if it was passed { self->name = TRACKED_NEW char[strlen(nameTemp) + 1]; strcpy(self->name, nameTemp); self->name[strlen(nameTemp)] = '\0'; } else self->name = NULL; self->value = value; } return (PyObject*)self; } static void EnumValue_dealloc(EnumValue *self) { if (self->name) delete [] self->name; self->ob_type->tp_free((PyObject*)self); } static int EnumValue_print(PyObject *self, FILE *fp, int flags) { // convert this object to a string PyObject *strObject = (flags & Py_PRINT_RAW) ? self->ob_type->tp_str(self) : self->ob_type->tp_repr(self); if (strObject == NULL) return -1; // failure const char* text = PyString_AsString(strObject); if (text == NULL) return -1; fprintf(fp, text); // and print it to the file return 0; } static PyObject *EnumValue_repr(PyObject *self) { EnumValue *obj = (EnumValue*)self; if (obj->name == NULL) { // no name, so just output our value return PyString_FromFormat("%s(%ld)", obj->ob_type->tp_name, obj->value); } else { // we have a name, so output it return PyString_FromString(obj->name); } } static PyObject* EnumValue_str(PyObject* self) { EnumValue *obj = (EnumValue*)self; if (obj->name == NULL) { // no name, so return our value return PyString_FromFormat("%s(%ld)", obj->ob_type->tp_name, obj->value); } else { // just return the name return PyString_FromString(obj->name); } } // forward def because the type object isn't defined yet bool IsEnumValue(PyObject *obj); long EnumValue_hash(PyObject *v) { // stolen from python's int class if (!IsEnumValue(v)) { PyErr_SetString(PyExc_TypeError, "hash was passed a non enum value (this would be weird)"); return 0; } long x = ((EnumValue*)v)->value; if (x == -1) x = -2; return x; } int EnumValue_nonzero(EnumValue *v) { return v->value != 0; } PyObject *EnumValue_and(PyObject *v, PyObject *w) { EnumValue *obj = NULL; long other = 0; if (IsEnumValue(v)) { obj = (EnumValue*)v; if (!PyInt_Check(w)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } other = PyInt_AsLong(w); } else if (IsEnumValue(w)) { obj = (EnumValue*)w; if (!PyInt_Check(v)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } other = PyInt_AsLong(v); } else { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } return PyInt_FromLong(obj->value & other); } PyObject *EnumValue_xor(PyObject *v, PyObject *w) { EnumValue *obj = NULL; long other = 0; if (IsEnumValue(v)) { obj = (EnumValue*)v; if (!PyInt_Check(w)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } other = PyInt_AsLong(w); } else if (IsEnumValue(w)) { obj = (EnumValue*)w; if (!PyInt_Check(v)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } other = PyInt_AsLong(v); } else { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } return PyInt_FromLong(obj->value ^ other); } PyObject *EnumValue_or(PyObject *v, PyObject *w) { EnumValue *obj = NULL; long other = 0; if (IsEnumValue(v)) { obj = (EnumValue*)v; if (!PyInt_Check(w)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } other = PyInt_AsLong(w); } else if (IsEnumValue(w)) { obj = (EnumValue*)w; if (!PyInt_Check(v)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } other = PyInt_AsLong(v); } else { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } return PyInt_FromLong(obj->value | other); } PyObject *EnumValue_int(EnumValue *v) { return PyInt_FromLong(v->value); } PyObject *EnumValue_long(EnumValue *v) { return PyLong_FromLong((v->value)); } PyObject *EnumValue_float(EnumValue *v) { return PyFloat_FromDouble((double)(v->value)); } PyObject *EnumValue_oct(EnumValue *v) { char buf[100]; long x = v->value; if (x < 0) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } if (x == 0) strcpy(buf, "0"); else _snprintf(buf, sizeof(buf), "0%lo", x); return PyString_FromString(buf); } PyObject *EnumValue_hex(EnumValue *v) { char buf[100]; long x = v->value; if (x < 0) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } _snprintf(buf, sizeof(buf), "0x%lx", x); return PyString_FromString(buf); } int EnumValue_coerce(PyObject **pv, PyObject **pw) { if (PyInt_Check(*pw)) { long x = PyInt_AsLong(*pw); *pw = NewEnumValue(NULL, x); Py_INCREF(*pv); return 0; } else if (PyLong_Check(*pw)) { double x = PyLong_AsDouble(*pw); if (x == -1.0 && PyErr_Occurred()) return -1; *pw = NewEnumValue(NULL, (long)x); Py_INCREF(*pv); return 0; } else if (PyFloat_Check(*pw)) { double x = PyFloat_AsDouble(*pw); *pw = NewEnumValue(NULL, (long)x); Py_INCREF(*pv); return 0; } else if (IsEnumValue(*pw)) { Py_INCREF(*pv); Py_INCREF(*pw); return 0; } return 1; // Can't do it } // we support some of the number methods PYTHON_START_AS_NUMBER_TABLE(EnumValue) 0, /*nb_add*/ 0, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_divide*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ 0, /*nb_power*/ 0, /*nb_negative*/ 0, /*nb_positive*/ 0, /*nb_absolute*/ (inquiry)EnumValue_nonzero, /*nb_nonzero*/ 0, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ (binaryfunc)EnumValue_and, /*nb_and*/ (binaryfunc)EnumValue_xor, /*nb_xor*/ (binaryfunc)EnumValue_or, /*nb_or*/ (coercion)EnumValue_coerce, /*nb_coerce*/ (unaryfunc)EnumValue_int, /*nb_int*/ (unaryfunc)EnumValue_long, /*nb_long*/ (unaryfunc)EnumValue_float, /*nb_float*/ (unaryfunc)EnumValue_oct, /*nb_oct*/ (unaryfunc)EnumValue_hex, /*nb_hex*/ 0, /*nb_inplace_add*/ 0, /*nb_inplace_subtract*/ 0, /*nb_inplace_multiply*/ 0, /*nb_inplace_divide*/ 0, /*nb_inplace_remainder*/ 0, /*nb_inplace_power*/ 0, /*nb_inplace_lshift*/ 0, /*nb_inplace_rshift*/ 0, /*nb_inplace_and*/ 0, /*nb_inplace_xor*/ 0, /*nb_inplace_or*/ 0, /* nb_floor_divide */ 0, /* nb_true_divide */ 0, /* nb_inplace_floor_divide */ 0, /* nb_inplace_true_divide */ PYTHON_END_AS_NUMBER_TABLE; PYTHON_COMPARE_DEFINITION(EnumValue, v, w) { long i = 0; long j = 0; if (IsEnumValue(v)) { i = ((EnumValue*)v)->value; if (PyInt_Check(w)) j = PyInt_AsLong(w); else if (PyFloat_Check(w)) j = (long)PyFloat_AsDouble(w); else if (PyLong_Check(w)) j = PyLong_AsLong(w); else if (IsEnumValue(w)) j = ((EnumValue*)w)->value; else { PyErr_SetString(PyExc_TypeError, "cannot compare EnumValue to a non number"); return 0; } } else if (IsEnumValue(w)) { j = ((EnumValue*)w)->value; if (PyInt_Check(v)) i = PyInt_AsLong(v); else if (PyFloat_Check(v)) i = (long)PyFloat_AsDouble(v); else if (PyLong_Check(v)) i = PyLong_AsLong(v); else if (IsEnumValue(v)) i = ((EnumValue*)v)->value; else { PyErr_SetString(PyExc_TypeError, "cannot compare EnumValue to a non number"); return 0; } } else { PyErr_SetString(PyExc_TypeError, "cannot compare EnumValue to a non number"); return 0; } if (i < j) PYTHON_COMPARE_LESS_THAN; else if (i > j) PYTHON_COMPARE_GREATER_THAN; PYTHON_COMPARE_EQUAL; } PYTHON_NO_INIT_DEFINITION(EnumValue) PYTHON_START_METHODS_TABLE(EnumValue) PYTHON_END_METHODS_TABLE; PYTHON_TYPE_START(EnumValue) 0, "PlasmaConstants.EnumValue", sizeof(EnumValue), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)EnumValue_dealloc, /* tp_dealloc */ EnumValue_print, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ PYTHON_DEFAULT_COMPARE(EnumValue), /* tp_compare */ EnumValue_repr, /* tp_repr */ PYTHON_DEFAULT_AS_NUMBER(EnumValue),/* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ EnumValue_hash, /* tp_hash */ 0, /* tp_call */ EnumValue_str, /* tp_str */ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_CHECKTYPES, /* tp_flags */ "A basic enumeration value", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ PYTHON_DEFAULT_METHODS_TABLE(EnumValue), /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ PYTHON_DEFAULT_INIT(EnumValue), /* tp_init */ 0, /* tp_alloc */ EnumValue_new /* tp_new */ PYTHON_TYPE_END; bool IsEnumValue(PyObject *obj) { return PyObject_TypeCheck(obj, &EnumValue_type); } PyObject *NewEnumValue(char const* name, long value) { PyObject *tempArgs = NULL; if (name) tempArgs = Py_BuildValue("(ls)", value, name); // args are value, name else tempArgs = Py_BuildValue("(l)", value); // args are value only EnumValue *newObj = (EnumValue*)EnumValue_type.tp_new(&EnumValue_type, tempArgs, NULL); Py_DECREF(tempArgs); // clean up the temps return (PyObject*)newObj; } // Now for the Enum base class struct Enum { PyObject_HEAD PyObject *values; // the values list PyObject *lookup; // the enum values, key is enum, value is enum value PyObject *reverseLookup; // the enum values, key is enum value, value is enum }; static PyObject *Enum_new(PyTypeObject *type, PyObject *, PyObject *) { Enum *self = (Enum*)type->tp_alloc(type, 0); if (self != NULL) { self->values = PyDict_New(); if (self->values == NULL) { Py_DECREF(self); return NULL; } self->lookup = PyDict_New(); if (self->lookup == NULL) { Py_DECREF(self); return NULL; } self->reverseLookup = PyDict_New(); if (self->reverseLookup == NULL) { Py_DECREF(self); return NULL; } } return (PyObject*)self; } static int Enum_traverse(Enum *self, visitproc visit, void *arg) { if (self->values && visit(self->values, arg) < 0) return -1; if (self->lookup && visit(self->lookup, arg) < 0) return -1; if (self->reverseLookup && visit(self->reverseLookup, arg) < 0) return -1; return 0; } static int Enum_clear(Enum *self) { Py_XDECREF(self->values); self->values = NULL; Py_XDECREF(self->lookup); self->lookup = NULL; Py_XDECREF(self->reverseLookup); self->reverseLookup = NULL; return 0; } static void Enum_dealloc(Enum *self) { Enum_clear(self); self->ob_type->tp_free((PyObject*)self); } static PyObject *Enum_getattro(PyObject *self, PyObject *attr_name) { if (!PyString_Check(attr_name)) { PyErr_SetString(PyExc_TypeError, "getattro expects a string argument"); PYTHON_RETURN_ERROR; } Enum *theEnum = (Enum*)self; PyObject *item = PyDict_GetItem(theEnum->lookup, attr_name); if (!item) { PyErr_Clear(); // wasn't in the dictionary, check to see if they want the values or lookup dict char *name = PyString_AsString(attr_name); if (strcmp(name, "values") == 0) { Py_INCREF(theEnum->values); return theEnum->values; } else if (strcmp(name, "lookup") == 0) { Py_INCREF(theEnum->lookup); return theEnum->lookup; } else if (strcmp(name, "reverseLookup") == 0) { Py_INCREF(theEnum->reverseLookup); return theEnum->reverseLookup; } return PyObject_GenericGetAttr(self, attr_name); // let the default method handle it } Py_INCREF(item); return item; } static int Enum_setattro(PyObject *self, PyObject *attr_name, PyObject *value) { if (!PyString_Check(attr_name)) { PyErr_SetString(PyExc_TypeError, "setattro expects a string argument"); return -1;; } Enum *theEnum = (Enum*)self; PyObject *item = PyDict_GetItem(theEnum->lookup, attr_name); if (!item) { PyErr_Clear(); // wasn't in the dictionary, check to see if they want the values or lookup dict char *name = PyString_AsString(attr_name); if (strcmp(name, "values") == 0) { PyErr_SetString(PyExc_RuntimeError, "Cannot set the value attribute"); return -1; } else if (strcmp(name, "lookup") == 0) { PyErr_SetString(PyExc_RuntimeError, "Cannot set the lookup attribute"); return -1; } else if (strcmp(name, "reverseLookup") == 0) { PyErr_SetString(PyExc_RuntimeError, "Cannot set the reverseLookup attribute"); return -1; } // they aren't trying to set an enum value or any of our special values, so let them return PyObject_GenericSetAttr(self, attr_name, value); // let the default method handle it } PyErr_SetString(PyExc_RuntimeError, "Cannot set any enum value"); return -1; } PYTHON_NO_INIT_DEFINITION(Enum) PYTHON_START_METHODS_TABLE(Enum) PYTHON_END_METHODS_TABLE; PYTHON_TYPE_START(Enum) 0, "PlasmaConstants.Enum", sizeof(Enum), /* tp_basicsize */ 0, /* tp_itemsize */ (destructor)Enum_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ 0, /* tp_compare */ 0, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ 0, /* tp_as_mapping */ 0, /* tp_hash */ 0, /* tp_call */ 0, /* tp_str */ Enum_getattro, /* tp_getattro */ Enum_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ "Enum base class", /* tp_doc */ (traverseproc)Enum_traverse, /* tp_traverse */ (inquiry)Enum_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ PYTHON_DEFAULT_METHODS_TABLE(Enum), /* tp_methods */ 0, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ PYTHON_DEFAULT_INIT(Enum), /* tp_init */ 0, /* tp_alloc */ Enum_new /* tp_new */ PYTHON_TYPE_END; // creates and sets up the enum base class void pyEnum::AddPlasmaConstantsClasses(PyObject *m) { PYTHON_CLASS_IMPORT_START(m); PYTHON_CLASS_IMPORT(m, EnumValue); PYTHON_CLASS_IMPORT(m, Enum); PYTHON_CLASS_IMPORT_END(m); } // makes an enum object using the specified name and values void pyEnum::MakeEnum(PyObject *m, const char* name, std::map values) { if (m == NULL) return; Enum *newEnum = (Enum*)Enum_type.tp_new(&Enum_type, NULL, NULL); if (newEnum == NULL) { std::string errorStr = "Could not create enum named "; errorStr += name; PyErr_SetString(PyExc_RuntimeError, errorStr.c_str()); return; } PyObject *valuesDict = newEnum->values; PyObject *lookupDict = newEnum->lookup; PyObject *reverseLookupDict = newEnum->reverseLookup; for (std::map::iterator curValue = values.begin(); curValue != values.end(); curValue++) { std::string key = curValue->first; int value = curValue->second; std::string enumValueName = name; enumValueName += "." + key; PyObject *newValue = NewEnumValue(enumValueName.c_str(), value); PyObject *valueObj = PyInt_FromLong((long)value); PyObject *keyObj = PyString_FromString(key.c_str()); PyDict_SetItem(valuesDict, valueObj, newValue); PyDict_SetItem(lookupDict, keyObj, newValue); PyDict_SetItem(reverseLookupDict, newValue, keyObj); Py_DECREF(keyObj); Py_DECREF(valueObj); Py_DECREF(newValue); } PyModule_AddObject(m, (char*)name, (PyObject*)newEnum); }