0

i am writing a Test to keep Data in a C++ map. I am using the Python C-Api.
Adding int values and getting the length of the map is no problem.
But when i get a value from the map and want to return it to Python than the Interpreter crashes. This is my Code:

    //some includes 
    int VerboseLog = 99;
    typedef map<const char*, int> SessionKeeper;
    static SessionKeeper openSessions;

    static PyObject* getSession(PyObject* self, PyObject* args) {
        cout << "get" << endl;
        const char *key;

        if (!PyArg_ParseTuple(args, "z*", &key)) {
            PyErr_SetString(PyExc_Exception, "UUID konnte nicht ausgelesen werden");
            PyErr_PrintEx(1);
            return NULL;
        }

        int r = openSessions.at(key);
        return Py_BuildValue("i", r);
    }

    static PyObject *addSession(PyObject* self, PyObject* args) {
        cout << "Hello" << endl;
        const char  *key;
        int toAppend;

        if (!PyArg_ParseTuple(args, "si", &key, &toAppend)) {
            PyErr_SetString(PyExc_Exception, "Ein Parameter konnte nicht ausgelesen werden");
            PyErr_PrintEx(1);
            return NULL;
        }

        openSessions[key] = toAppend;
        cout << openSessions.size() << endl;
        cout << openSessions[key] << endl;
        Py_RETURN_NONE;
    }

    static PyObject* length(PyObject *self){
        cout << "length: ";
        int i = openSessions.size();
        return Py_BuildValue("i",i);
    }

    static PyMethodDef SessionKeeper_methods[] = {
        /*Note the third entry (METH_VARARGS). This is a flag telling the interpreter the calling convention
        to be used for the C function. It should normally always be METH_VARARGS or METH_VARARGS | METH_KEYWORDS;
        a value of 0 means that an obsolete variant of PyArg_ParseTuple() is used.*/
        { "length", (PyCFunction)length, METH_VARARGS, "return length of a Session" },
        { "addSession", (PyCFunction)addSession, METH_VARARGS, "add a Session." },
        { "getSession", (PyCFunction)getSession, METH_VARARGS, "get a Session" },
        { NULL }
    };

    static PyModuleDef sessionKeeperMod = {
        PyModuleDef_HEAD_INIT,
        u8"SessionKeeper",
        NULL,
        -1,
        SessionKeeper_methods
    };

    static PyModuleDef CpluplusModules_ModDef = {
        PyModuleDef_HEAD_INIT,
        u8"CPlusPlusMouldes",
        u8"Enthält alle Erweietrungen",
        -1,
        NULL, NULL, NULL, NULL, NULL
    };

    PyMODINIT_FUNC PyInit_CPlusPlusModules(void) {
            PyObject* m, *sessionMod;

            if (PyType_Ready(&TAModulType) < 0)
                return NULL;

            if (PyType_Ready(&DatabaseReader_Type) < 0)
                return NULL;

            if (!(sessionMod = PyModule_Create(&sessionKeeperMod))) {
                cout << "fail" << endl;
                return NULL;
            }

            m = PyModule_Create(&CpluplusModules_ModDef);
            if (m == NULL)
                return NULL;

            Py_INCREF(&TAModulType);
            Py_INCREF(&DatabaseReader_Type);

            PyModule_AddObject(m, "TAModul", (PyObject *)&TAModulType);
            PyModule_AddObject(m, "DBReader", (PyObject *)&DatabaseReader_Type);
            PyModule_AddObject(m, "SessionKeeper", sessionMod);

            return m;
    }

The other Modules (DBReader and TAModule) workes fine. The goal will be to safe PythonObjects (containing DbReader Objects and TAModul Objects) in the map.

But bacck to my question why do the getSession crashes the Interpreter on return? And why do the length function works fine.

1 Answers1

0

map<const char*,...> will match based on the address of the string rather than the contents. Therefore it's pretty likely that at will fail and throw an C++ out_of_range exception.

You should start by wrapping the line

int r = openSessions.at(key);

with a try {} catch block, to stop the out_of_range exception propagating through the Python interpreter (which isn't C++ so can't handle it).

You should then change the map to match the key on the string contents. The easiest way would be to use map<std::string,int>.

DavidW
  • 29,336
  • 6
  • 55
  • 86