PyBind11:boost::multiprecision::cpp_int 到 Python

Posted

技术标签:

【中文标题】PyBind11:boost::multiprecision::cpp_int 到 Python【英文标题】:PyBind11: boost::multiprecision::cpp_int to Python 【发布时间】:2019-02-17 21:47:41 【问题描述】:

我有兴趣使用pybind11 来优化一些使用 C++ 的 Python 计算。 casting documentation 对我来说没有多大意义,我想知道是否有人知道如何将 boost 数据类型(特别是 cpp_int)转换为 Python 数据类型,以便我可以返回计算。我正在尝试做的一个简单示例是阶乘:

#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

#include <boost/multiprecision/cpp_int.hpp>


using boost::multiprecision::cpp_int;
namespace py = pybind11;

py::int_ fact(int i) 
    cpp_int prod = 1;
    while(i-- >= 1)
        prod *= (i+1);
    
    return py::cast(prod);


PYBIND11_MODULE(fact, m) 
  m.def("fact", &fact,R"pbdoc(
        Returns the factorial of a number.
    )pbdoc");

上面可以编译,但是当我去使用它时,我得到了

TypeError: Unregistered type : boost::multiprecision::number<boost::multiprecision::backends::cpp_int_backend<0u, 0u, (boost::multiprecision::cpp_integer_type)1, (boost::multiprecision::cpp_int_check_type)0, std::allocator<unsigned long long> >, (boost::multiprecision::expression_template_option)1>

所以我认为 py::cast 无法正常工作...我的笔记本电脑运行 Windows 10,而我使用的是 Anaconda Python 3.7

C:\Users\15734>python
Python 3.7.1 (default, Dec 10 2018, 22:54:23) [MSC v.1915 64 bit (AMD64)] :: Anaconda, Inc. on win32
Type "help", "copyright", "credits" or "license" for more information.

和Stephan T. Lavavej's MinGW C++ 8.2.0

C:\Users\15734>g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=c:/mingw/bin/../libexec/gcc/x86_64-w64-mingw32/8.2.0/lto-wrapper.exe
Target: x86_64-w64-mingw32
Configured with: ../src/configure --enable-languages=c,c++ --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --disable-multilib --prefix=/c/temp/gcc/dest --with-sysroot=/c/temp/gcc/dest --disable-libstdcxx-pch --disable-libstdcxx-verbose --disable-nls --disable-shared --disable-win32-registry --with-tune=haswell --enable-threads=posix --enable-libgomp
Thread model: posix
gcc version 8.2.0 (GCC)

我还为位于“C:\ProgramFiles(x86)\MicrosoftVisualStudio\2017\BuildTools\VC\Redist\MSVC\14.16.27012\onecore\x64\Microsoft 的 vcruntime140.dll 使用Build Tools for Visual Studio 2017 .VC141.CRT”(我将其复制并粘贴到“C:\MinGW\lib”)。我还将“C:\Anaconda3\Lib\distutils\cygwinccompiler.py”中的所有字符串“gcc”更改为“g++”(我没有用gcc 在其中)。

关键字: “pybind11”; “促进” ; “C++ 和 Python”; "boost::multiprecision::cpp_int"

【问题讨论】:

cpp_int(对于某些后端)只是 C 库(mpz,也许?)的包装器,因此直接包装 C 可能更容易。 @user14717 如果我包装了 mpz 我将如何在 C++ 中使用它并通过 pybind11 返回它? 好吧,你根本不会在 C++ 中使用它。如果你需要在 C++ 和 Python 中都使用它,我的建议是行不通的。 如果你直接从 C->Python 开始,你会构建一个 python 扩展模块。 也许您可以尝试将struct inty 替换为cpp_int?。您可以将 cpp_int 转换为 base16 字符串并将其传递给 PyLong_FromString 以从 C++-->Python 和 Python-->C++ 转换,方法是使用 PyNumber_ToBase 将 PyNumber 转换为字符串 base16,并基于此 Python 字符串对象构造一个 cpp_int。 【参考方案1】:

我得到了这个工作:

#include <boost/multiprecision/cpp_int.hpp>
#include <iomanip>
#include <pybind11/pybind11.h>
#include <sstream>

using cpp_int = boost::multiprecision::cpp_int;
namespace py = pybind11;

namespace pybind11

namespace detail

    template <>
    struct type_caster<cpp_int> 
        /**
         * This macro establishes the name 'cpp_int' in
         * function signatures and declares a local variable
         * 'value' of type cpp_int
         */
        PYBIND11_TYPE_CASTER(cpp_int, _("cpp_int"));

        /**
         * Conversion part 1 (Python->C++): convert a PyObject into a cpp_int
         * instance or return false upon failure. The second argument
         * indicates whether implicit conversions should be applied.
         */
        bool load(handle src, bool)
        
            // Convert into base 16 string (PyNumber_ToBase prepend '0x')
            PyObject* tmp = PyNumber_ToBase(src.ptr(), 16);
            if (!tmp) return false;

            std::string s = py::cast<std::string>(tmp);
            value = cpp_ints; // explicit cast from string to cpp_int,
                                // don't need a base here because
                                // `PyNumber_ToBase` already prepended '0x'
            Py_DECREF(tmp);

            /* Ensure return code was OK (to avoid out-of-range errors etc) */
            return !PyErr_Occurred();
        

        /**
         * Conversion part 2 (C++ -> Python): convert an cpp_int instance into
         * a Python object. The second and third arguments are used to
         * indicate the return value policy and parent object (for
         * ``return_value_policy::reference_internal``) and are generally
         * ignored by implicit casters.
         */
        static handle cast(const cpp_int& src, return_value_policy, handle)
        
            // Convert cpp_int to base 16 string
            std::ostringstream oss;
            oss << std::hex << src;
            return PyLong_FromString(oss.str().c_str(), nullptr, 16);
        
    ;
 // namespace detail
 // namespace pybind11

py::int_ fact(int i)

    cpp_int prod = 1;
    while (i-- > 1) prod *= i + 1;
    return py::cast(prod);


PYBIND11_MODULE(python_example, m)

    m.def("fact", &fact, R"pbdoc(
        Returns the factorial of a number.
    )pbdoc");

可能不是最快的方法,因为它首先将cpp_int 转换为一个临时的 base 16 字符串,然后再将此字符串转换为 Python 整数。

【讨论】:

以上是关于PyBind11:boost::multiprecision::cpp_int 到 Python的主要内容,如果未能解决你的问题,请参考以下文章

from _dlib_pybind11 import * ModuleNotFoundError: No module named '_dlib_pybind11'

如何将浮点数传递给期望 int 的 pybind11 函数

返回多个 py::array 而不在 pybind11 中复制

使用 Pybind11 包装 STL 容器返回类型

如何通过pybind11在python中捕获C++的异常?

Pybind11:外部类型作为返回值