首页 > 解决方案 > Python C-Api……如何从 C 创建一个“IntEnum”对象

问题描述

我想使用 示例中的 Tcl-C-Code作为模板在C中创建一个名为MqHandShakeE的IntEnum子类

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;
}

我的目标是调用一个 python 函数......</p>

myfunc … MqHandShakeE.OK …

C 常数MQ_HANDSHAKE_START是一个整数

仅 Python 解决方案:

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

这是类型安全的,因为WAIT.NO具有类型WAIT和值0……我可以检查……从下往上的子模块方法不是类型安全的…… WAIT.NOas 子模块具有类型int

标签: pythoncapi

解决方案


在我发布了 C++ 示例后,我意识到 OP 已经用 C(不是 C++)标记了这个问题。对不起,我的错。这是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;
}

在 VS2013 中使用 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!

此示例建立了一个模块,该模块MQ_HANDSHAKE- 必须在调用之前附加到 Python 表(使用PyImport_AppendInittab()PyInitialize()- 必须在 Python 代码中导入(使用import MQ_HANDSHAKE)。


C++ 代码的原始答​​案:

我查看了我们的 Python 包装器并为 OPs 案例制作了一个小示例:

#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;
}

在 VS2013 中使用 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!

我必须承认,我通过在我们的生产代码中作弊来模仿这个示例,我们曾经在挖掘在线资源时耐心地做这件事。对于基本介绍,我推荐1. Embedding Python in Another Application

必须先在 Python 代码中导入根模块,然后才能使用它(import mq在 Python 示例代码中)。在我们的生产代码中,我们在单独的前面调用 中完成了它PyRun_SimpleString(),因此我们的 Python 应用程序程序员甚至不需要关心这个。

我将实现拆分为多个模块(mqmq.Handshake)。mqHandshake通过使用模块变量STARTOK和建立一个模块,这当然可以更短地完成ERROR


推荐阅读