代码之家  ›  专栏  ›  技术社区  ›  ar2015

将可变数量的参数传递给嵌入式python API

  •  0
  • ar2015  · 技术社区  · 7 年前

    今天我在C++中阅读了嵌入式Python。

    https://docs.python.org/3/extending/embedding.html

    所以,我可以调用C++中的Python代码。

    但在API示例中调用python的方式在我看来并不酷。

    我正在考虑用C++任意方式调用Python函数,如:

    py_call(script_path,module_name,str1,int2,long3,float4,str5,double6);
    

    py_call(script_path,module_name,x,y,z,title);
    

    但我需要用 parameter pack . 这是我第一次看到参数包。我就困在这里不知道怎么换 argc argv 以下代码中的参数:

    template<typename T, typename... Targs>
    void py_call(
            const string &script,
            const string &module,
            T value, Targs... Fargs
        )
    {
        PyObject *pName, *pModule, *pFunc;
        PyObject *pArgs, *pValue;
        int i;
    
        pName = PyUnicode_DecodeFSDefault(script.c_str());
        /* Error checking of pName left out */
    
        pModule = PyImport_Import(pName);
        Py_DECREF(pName);
        if (pModule != NULL) {
            pFunc = PyObject_GetAttrString(pModule, module.c_str());
            /* pFunc is a new reference */
    
            if (pFunc && PyCallable_Check(pFunc)) {
                pArgs = PyTuple_New(argc - 3);
                for (i = 0; i < argc - 3; ++i) {
                    pValue = PyLong_FromLong(atoi(argv[i + 3]));
                    if (!pValue) {
                        Py_DECREF(pArgs);
                        Py_DECREF(pModule);
                        fprintf(stderr, "Cannot convert argument\n");
                        return 1;
                    }
                    /* pValue reference stolen here: */
                    PyTuple_SetItem(pArgs, i, pValue);
                }
                pValue = PyObject_CallObject(pFunc, pArgs);
                Py_DECREF(pArgs);
                if (pValue != NULL) {
                    printf("Result of call: %ld\n", PyLong_AsLong(pValue));
                    Py_DECREF(pValue);
                }
                else {
                    Py_DECREF(pFunc);
                    Py_DECREF(pModule);
                    PyErr_Print();
                    fprintf(stderr,"Call failed\n");
                    return 1;
                }
            }
            else
            {
                if (PyErr_Occurred())
                    PyErr_Print();
                fprintf(stderr, "Cannot find function \"%s\"\n", module.c_str());
            }
            Py_XDECREF(pFunc);
            Py_DECREF(pModule);
        }
        else
        {
            PyErr_Print();
            fprintf(stderr, "Failed to load \"%s\"\n",script.c_str());
            return 1;
        }
    }
    

    附言。 argc=sizeof...(Fargs)+1 argc=sizeof...(Fargs) 取决于功能实现。

    1 回复  |  直到 7 年前
        1
  •  1
  •   joe_chip    7 年前

    我建议分块建造 argv 其余代码中的元组。为了构造这个元组,需要遍历参数包,并为每个参数创建 PyObject 通过超载。C++ 11版本可以看起来像这样:

    void to_py_tuple_impl(PyObject*, size_t) {}
    
    template<typename ParamType, typename... ParamTypesTail>
    void to_py_tuple_impl(PyObject* tpl, size_t index, const ParamType& param, const ParamTypesTail&... tail)
    {
        // error checking omitted for clarity
        PyTuple_SetItem(tpl, index, to_py_object(param));
        to_py_tuple_impl(tpl, index + 1, tail...);
    }
    
    template<typename... ParamTypes>
    PyObject* to_py_tuple(const ParamTypes&... args)
    {
        PyObject* tpl = PyTuple_New(sizeof...(ParamTypes));
        to_py_tuple_impl(tpl, 0, args...);
        return tpl;
    }
    

    如果必须坚持C++ 11,则需要使用递归函数来迭代。 args -那就是 to_py_tuple_impl 做。我已经把它包装好了 to_py_tuple 为了简单起见。

    此代码调用 to_py_object 对于每个参数,以便将其转换为 PyObject* 并将该对象插入元组。

    复制对象 可以重载以支持多种类型,例如:

    PyObject* to_py_object(const std::string& str)
    {
        return PyUnicode_FromStringAndSize(str.c_str(), str.size());
    }
    
    PyObject* to_py_object(const char* str)
    {
        retrurn PyUnicode_FromString(str);
    }
    

    使用模板和 std::enable_if :

    // Converts all integer types:
    template<typename T>
    std::enable_if_t<std::is_integral<T>::value, PyObject*> to_py_object(T value)
    {
        return PyLong_FromLong(value);
    }
    
    // Converts all floating point types:
    template<typename T>
    std::enable_if_t<std::is_floating_point<T>::value, PyObject*> to_py_object(T value)
    {
        return PyFloat_FromDouble(value);
    }
    

    现在,你可以把它插到你的 py_call 功能:

    template<typename T, typename... Targs>
    void py_call(
            const string &script,
            const string &module,
            T value, Targs... Fargs
        )
    {
        PyObject *pName, *pModule, *pFunc;
        PyObject *pArgs, *pValue;
        int i;
    
        pName = PyUnicode_DecodeFSDefault(script.c_str());
        /* Error checking of pName left out */
    
        pModule = PyImport_Import(pName);
        Py_DECREF(pName);
        if (pModule != NULL) {
            pFunc = PyObject_GetAttrString(pModule, module.c_str());
            /* pFunc is a new reference */
    
            if (pFunc && PyCallable_Check(pFunc)) {
                pArgs = to_py_tuple(Fargs...);
                pValue = PyObject_CallObject(pFunc, pArgs);
    
                ...