这仅部分回答了您的问题。因为直接的答案是:不,如果没有浪费地转换为字符串,就不可能以干净的方式进行,因为mpmath
它是一个纯粹的python库,没有任何部分写入C
or C++
,因此即使您尝试跳过“浪费的转换”寻求使用某种二进制兼容性,您的代码将非常脆弱:每当某些python
或mpmath
内部结构发生如此轻微的变化时,它都会中断。
但是我需要完全相同的东西。所以我决定注册一个自动转换,通过boost::python
它使用字符串检查和转换。实际上在 python 中,你也可以mpmath.mpf
从字符串创建对象,所以它非常相似,除了在下面的代码中它更快,因为它是写在里面C++
的。
所以这对我有用:
#include <boost/python.hpp>
#include <iostream>
#include <limits>
#include <sstream>
#include <boost/math/constants/constants.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
namespace py = ::boost::python;
using Prec80 = boost::multiprecision::number<boost::multiprecision::cpp_bin_float<80>>;
template<typename ArbitraryReal>
struct ArbitraryReal_to_python {
static PyObject* convert(const ArbitraryReal& val){
std::stringstream ss{};
ss << std::setprecision(std::numeric_limits<ArbitraryReal>::digits10+1) << val;
py::object mpmath = py::import("mpmath");
mpmath.attr("mp").attr("dps")=int(std::numeric_limits<ArbitraryReal>::digits10+1);
py::object result = mpmath.attr("mpf")(ss.str());
return boost::python::incref( result.ptr() );
}
};
template<typename ArbitraryReal>
struct ArbitraryReal_from_python {
ArbitraryReal_from_python(){
boost::python::converter::registry::push_back(&convertible,&construct,boost::python::type_id<ArbitraryReal>());
}
static void* convertible(PyObject* obj_ptr){
// Accept whatever python is able to convert into float
// This works with mpmath numbers. However if you want to accept strings as numbers this checking code can be a little longer to verify if string is a valid number.
double check = PyFloat_AsDouble(obj_ptr);
return (PyErr_Occurred()==nullptr) ? obj_ptr : nullptr;
}
static void construct(PyObject* obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data){
std::istringstream ss{ py::call_method<std::string>(obj_ptr, "__str__") };
void* storage=((boost::python::converter::rvalue_from_python_storage<ArbitraryReal>*)(data))->storage.bytes;
new (storage) ArbitraryReal;
ArbitraryReal* val=(ArbitraryReal*)storage;
ss >> *val;
data->convertible=storage;
}
};
struct Var
{
Prec80 value{"-71.23"};
Prec80 get() const { return value; };
void set(Prec80 val) { value = val; };
};
BOOST_PYTHON_MODULE(pysmall)
{
ArbitraryReal_from_python<Prec80>();
py::to_python_converter<Prec80,ArbitraryReal_to_python<Prec80>>();
py::class_<Var>("Var" )
.add_property("val", &Var::get, &Var::set);
}
现在你用这个命令编译这段代码:
g++ -O1 -g pysmall.cpp -o pysmall.so -std=gnu++17 -fPIC -shared -I/usr/include/python3.7m/ -lboost_python37 -lpython3.7m -Wl,-soname,"pysmall.so"
这是一个示例python
会话:
In [1]: import pysmall
In [2]: a=pysmall.Var()
In [3]: a.val
Out[3]: mpf('-71.2299999999999999999999999999999999999999999999999999999999999999999999999999997072')
In [4]: a.val=123.12
In [5]: a.val
Out[5]: mpf('123.120000000000000000000000000000000000000000000000000000000000000000000000000000003')
C++
代码不关心 mpmath 是否已经在 python 中导入。如果是,则获取现有库句柄,如果不是,则导入它。如果您发现此代码段有任何改进空间,请告诉我!
在我写这篇文章时,这里有一些有用的参考资料:
- https://misspent.wordpress.com/2009/09/27/how-to-write-boost-python-converters/
- https://github.com/bluescarni/mppp/blob/master/include/mp%2B%2B/extra/pybind11.hpp(但我不想用pybind11,只是
boost::python
)
编辑:我现在已经在 YADE 中完成了它的实现,它适用于 EIGEN 和 CGAL 库。关于这个问题的部分在文件ToFromPythonConverter.hpp