0

I want to create an IntEnum subclass called MqHandShakeE in C using Tcl-C-Code from the example as template

int NS(Get_MqHandShakeE_FromObj) (Tcl_Interp *interp, Tcl_Obj *enumE, enum MqHandShakeE *ret) {
  int index;
  const static struct LookupEnumE keys[] = {
    { "START",                MQ_HANDSHAKE_START   },
    { "OK",                   MQ_HANDSHAKE_OK      },
    { "ERROR",                MQ_HANDSHAKE_ERROR   },
    { NULL,                   0                    }
  };
  TclErrorCheck (Tcl_GetIndexFromObjStruct (interp, enumE, &keys,
      sizeof(struct LookupClassS), "enum", TCL_EXACT, &index));
  *ret = keys[index].val;
  return TCL_OK;
}

my Goal is to call a python function with…

myfunc … MqHandShakeE.OK …

the C-constant MQ_HANDSHAKE_START is an INTEGER

Python only solution:

from enum import IntEnum
class WAIT(IntEnum):
  NO  = 0
  ONCE = 1
  FOREVER = 2

this is type-safe because WAIT.NO has type WAIT and value 0… this i can check… the sub-module approach from down is not type-safe… the WAIT.NO as sub-module has type int

2
  • When I write Python wrappers for our C++ code, concerning enums, I make a (sub-)module for each enum and then add int variables for the resp. enumerators. (Something like enum seems not to exist in Python.) May be, it would be possible to sub-class one of the built-in Python integer types but the module/variable trick does properly in our case. Hence, I never tried the sub-classing. Commented Nov 19, 2018 at 7:00
  • I test submodul approach… the problem is… it is NOT typesafe Commented Nov 22, 2018 at 18:31

3 Answers 3

2

I found this code as an excellent reference to implement Enum using python c-API.

void register_enum(PyObject *py_module, const char *enum_name, PyObject *py_constants_dict) {
        PyObject *py_enum_class = NULL;
    {
        
        PyObject *py_enum_module = PyImport_ImportModule("enum");
        if (py_enum_module == NULL) {
            Py_CLEAR(py_constants_dict);
        }

        py_enum_class = PyObject_CallMethod(py_enum_module,
                "IntEnum", "sO", enum_name,
                py_constants_dict);

        Py_CLEAR(py_constants_dict);
        Py_CLEAR(py_enum_module);
    }

    if (py_enum_class && PyModule_AddObject(py_module, enum_name, py_enum_class) < 0) {
        Py_CLEAR(py_enum_class);
    }

}

To use the function above, call it the following:

PyObject* py_constants_dict = PyDict_New(); // empty for the sake of example 
    PyDict_SetItemString(py_constants_dict, "test1",  PyLong_FromLong(1) );
    PyDict_SetItemString(py_constants_dict, "test2",  PyLong_FromLong(2) );
    register_enum (m , "TestEnum", py_constants_dict);

It is equivalent to this python code:

import enum 
class TestEnum(enum.IntEnum):
   test1 = 1
   test2 = 2
Sign up to request clarification or add additional context in comments.

1 Comment

This has helped my case for passing enum values to a function.
1

After I had published a sample for C++, I realized that OP had tagged the question with C (not C++). Sorry, my fault. Here a sample in C:

#include <Python.h>

#include <assert.h>
#include <stdio.h>

/* sample enum in C */
enum MQ_HANDSHAKE {
  MQ_HANDSHAKE_START,
  MQ_HANDSHAKE_OK,
  MQ_HANDSHAKE_ERROR
};

/* make Python binding for MQ_HANDSHAKE */

static struct PyModuleDef moduleMQ_HANDSHAKE = {
  PyModuleDef_HEAD_INIT,
  "MQ_HANDSHAKE", /* name of module */
  NULL,           /* module documentation, may be NULL */
  -1,             /* size of per-interpreter state of the module,
                   * or -1 if the module keeps state in global variables.
                   */
  NULL            /* function table (no functions) */
};

static PyObject* initModuleMQ_HANDSHAKE(void)
{
  static PyObject *pSelf = NULL;
  if (!pSelf) {
    pSelf = PyModule_Create(&moduleMQ_HANDSHAKE);
    PyModule_AddObject(pSelf, "START", PyLong_FromLong(MQ_HANDSHAKE_START));
    PyModule_AddObject(pSelf, "OK", PyLong_FromLong(MQ_HANDSHAKE_OK));
    PyModule_AddObject(pSelf, "ERROR", PyLong_FromLong(MQ_HANDSHAKE_ERROR));
  }
  return pSelf;
}

/* adds module MQ_HANDSHAKE to Python modules table.
 */
void appendModuleMQ_HANDSHAKE(void)
{
  assert(!Py_IsInitialized());
  PyImport_AppendInittab("MQ_HANDSHAKE", &initModuleMQ_HANDSHAKE);
}

/* test program */
int main()
{
  /* initialize Python extension MQ_HANDSHAKE */
  appendModuleMQ_HANDSHAKE();
  /* initialize Python interpreter */
  Py_Initialize();
  /* sample Python program */
  static const char *const pyProgram
    = "print(\"Hello world (from Python).\")\n"
      "\n"
      "# import Python extension MQ_HANDSHAKE\n"
      "import MQ_HANDSHAKE\n"
      "\n"
      "# test whether it works\n"
      "def printHandshake(value):\n"
      "  if value == MQ_HANDSHAKE.START:\n"
      "    print(\"MQ_HANDSHAKE_START\")\n"
      "  elif value == MQ_HANDSHAKE.OK:\n"
      "    print(\"MQ_HANDSHAKE_OK\")\n"
      "  elif value == MQ_HANDSHAKE.ERROR:\n"
      "    print(\"MQ_HANDSHAKE_ERROR\")\n"
      "  else:\n"
      "    print(\"Illegal MQ_HANDSHAKE value!\")\n"
      "\n"
      "printHandshake(MQ_HANDSHAKE.START)\n"
      "printHandshake(MQ_HANDSHAKE.OK)\n"
      "printHandshake(MQ_HANDSHAKE.ERROR)\n"
      "printHandshake(0)\n"
      "printHandshake(1)\n"
      "printHandshake(2)\n"
      "printHandshake(42)\n";
  /* run Python interpreter */
  const int ret = PyRun_SimpleString(pyProgram);
  if (ret) {
    fprintf(stderr, "Execution in PyRun_SimpleString() failed!\n");
  }
  /* done */
  return ret;
}

Compiled and tested in VS2013 with Python 3.6:

Hello world (from Python).
MQ_HANDSHAKE_START
MQ_HANDSHAKE_OK
MQ_HANDSHAKE_ERROR
MQ_HANDSHAKE_START
MQ_HANDSHAKE_OK
MQ_HANDSHAKE_ERROR
Illegal MQ_HANDSHAKE value!

This sample establishs a module MQ_HANDSHAKE which - has to be appended to the Python tables (using PyImport_AppendInittab()) before PyInitialize() has been called - has to be imported in the Python code (using import MQ_HANDSHAKE).


The original answer with code in C++:

I took a look at our Python wrappers and made a little sample for OPs case:

#include <Python.h>

#include <cassert>
#include <iostream>

// sample enum in C/C++
enum MQ_HANDSHAKE {
  MQ_HANDSHAKE_START,
  MQ_HANDSHAKE_OK,
  MQ_HANDSHAKE_ERROR
};

namespace Py {

namespace MQ {

// make Python binding for MQ_HANDSHAKE

namespace HANDSHAKE {

static struct PyModuleDef module = {
  PyModuleDef_HEAD_INIT,
  "mq.Handshake", // name of module
  nullptr,        // module documentation, may be NULL
  -1,             /* size of per-interpreter state of the module,
                   * or -1 if the module keeps state in global variables.
                   */
  nullptr         // function table (no functions)
};

static PyObject* init()
{
  static PyObject *pSelf = nullptr;
  if (!pSelf) {
    pSelf = PyModule_Create(&module);
    PyModule_AddObject(pSelf, "START", PyLong_FromLong(MQ_HANDSHAKE_START));
    PyModule_AddObject(pSelf, "OK", PyLong_FromLong(MQ_HANDSHAKE_OK));
    PyModule_AddObject(pSelf, "ERROR", PyLong_FromLong(MQ_HANDSHAKE_ERROR));
  }
  return pSelf;
}

} // namespace HANDSHAKE

// make module MQ

static struct PyModuleDef module = {
  PyModuleDef_HEAD_INIT,
  "mq",     // name of module
  nullptr,  // module documentation, may be NULL
  -1,       /* size of per-interpreter state of the module,
             * or -1 if the module keeps state in global variables.
             */
  nullptr   // function table (no functions)
};

// initializes module mq
static PyObject* init()
{
  static PyObject *pSelf = nullptr;
  if (!pSelf) {
    pSelf = PyModule_Create(&module);
    PyModule_AddObject(pSelf, "Handshake", HANDSHAKE::init());

  }
  return pSelf;
}

// adds module mq to Python modules table.
void append()
{
  assert(!Py_IsInitialized());
  PyImport_AppendInittab("mq", &init);
}

} // namespace MQ

} // namespace Py

// test program
int main()
{
  // initialize Python extension mq
  Py::MQ::append();
  // initialize Python interpreter
  Py_Initialize();
  // sample Python program
  static const char *const pyProgram
    = "print(\"Hello world (from Python).\")\n"
      "\n"
      "# import Python extension mq\n"
      "import mq\n"
      "\n"
      "# test whether it works\n"
      "def printHandshake(value):\n"
      "  if value == mq.Handshake.START:\n"
      "    print(\"MQ_HANDSHAKE_START\")\n"
      "  elif value == mq.Handshake.OK:\n"
      "    print(\"MQ_HANDSHAKE_OK\")\n"
      "  elif value == mq.Handshake.ERROR:\n"
      "    print(\"MQ_HANDSHAKE_ERROR\")\n"
      "  else:\n"
      "    print(\"Illegal MQ_HANDSHAKE value!\")\n"
      "\n"
      "printHandshake(mq.Handshake.START)\n"
      "printHandshake(mq.Handshake.OK)\n"
      "printHandshake(mq.Handshake.ERROR)\n"
      "printHandshake(0)\n"
      "printHandshake(1)\n"
      "printHandshake(2)\n"
      "printHandshake(42)\n";
  // run Python interpreter
  const int ret = PyRun_SimpleString(pyProgram);
  if (ret) {
    std::cerr << "Execution in PyRun_SimpleString() failed!\n";
  }
  // done
  return ret;
}

Compiled and tested in VS2013 with Python 3.6:

Hello world (from Python).
MQ_HANDSHAKE_START
MQ_HANDSHAKE_OK
MQ_HANDSHAKE_ERROR
MQ_HANDSHAKE_START
MQ_HANDSHAKE_OK
MQ_HANDSHAKE_ERROR
Illegal MQ_HANDSHAKE value!

I must admit that I resembled this sample by cheating in our production code which we once did patiently while digging through the online resources. For a basic introduction, I recommend 1. Embedding Python in Another Application.

The root module has to be imported in the Python code before it can be used (import mq in the Python sample code). In our productive code, we did it in a separate preceding call of PyRun_SimpleString(), so that our Python application programmers even don't need to care about this.

I splitted the implementation into multiple modules (mq and mq.Handshake). This surely could be done even shorter by establishing a module mqHandshake with the module variables START, OK, and ERROR.

2 Comments

@AndreasOtto I overlooked the C tag. However, it looks quite similar in C. I extended the answer with a C sample (implementing the mentioned alternative with only one module).
Hi, I tested this… using > PyModule_AddIntConstant(pSelf, "START", MQ_HANDSHAKE_START) the problem is… the module const has type int this is no improvement adding regular toplevel > PyModule_AddIntConstant(pSelf, "HANDSHAKE_START)",MQ_HANDSHAKE_START)
0

this is my answer… to create a type-safe IntEnum class in python-C-api.

Python C-API… how to write python code in C

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.