用SWIG暴露boost uuid

时间:2016-03-07 18:20:38

标签: python c++ boost swig uuid

我的现有工作代码取决于boost::uuids::uuid。现在我试图从中生成一个python模块。 SWIG成功生成了所有重要的类和函数。但是我遇到了带有或返回boost uuid的函数的问题。

我想在boost uuid和python uuid之间进行转换。我可以用任何uuid.i吗?我看到有一个uuid python模块。我知道我可以使用uuid.iPyImport_ImportModule("uuid")导入该模块。

但是如何在typemap内实例化和使用python的uuid类?

1 个答案:

答案 0 :(得分:0)

通过PyImport_ImportModule调用,您可以通过正确的方式完成任务。您需要做的是弄清楚如何编组两种类型之间的UUID,然后为每个方向编写一个类型图。我最终通过字符串表示,因为这是我在视图中最简单的可移植方式。那是在两个方向上使用uuid_io.hpp的IO运算符。

当我们像这样包装它时,Python代码永远不会看到提升类型,反之亦然。

我假设你在这里瞄准Python 3.4,但我所做的一切都应该是简单的调整以适应旧的Python。

首先,我将一个C ++头文件放在一起,以演示我实现的包装:

#include <boost/uuid/uuid.hpp>
#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <iostream>

inline boost::uuids::uuid testout() {
    static boost::uuids::random_generator gen;
    return gen();
}

inline void testin(const boost::uuids::uuid& in) {
    std::cout << in << "\n";
}

那里没什么聪明的,每个传递对象的方向都只有一个函数。

接下来我写了一些Python来练习我想要制作的界面:

import test
import uuid

a=test.testout()
print(type(a))
print(a)

b=uuid.uuid4()
print(type(b))
print(b)

test.testin(a)
test.testin(b)

最后一个SWIG接口,一旦我编写了实际的UUID包装,就会生成这个模块。

%module test

%{
#include "test.hh"
%}

%include "boost_uuid.i"

%include "test.hh"

有了这一切,我们现在可以编写一个适用于我们场景的实现boost_uuid.i:

%{
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid.hpp>
#include <sstream>

namespace {
    PyObject *py_uuid = nullptr;
}   
%}

%init %{
    py_uuid = PyImport_ImportModule("uuid"); // Handle error
%}

%typemap(in) const boost::uuids::uuid& (boost::uuids::uuid tmp) {
    PyObject *str = PyObject_Str($input);
    assert(str); // TODO: check properly
    const char *uuid_str = PyUnicode_AsUTF8(str); // Note: Python 3.x, adjust as needed
    assert(uuid_str); // TODO: check me
    std::istringstream in(uuid_str);
    Py_DECREF(str);
    in >> tmp; // TODO: Check return!
    $1 = &tmp;
}

%typemap(out) boost::uuids::uuid {
    // Check this actually works!
    static PyObject *uuid_ctor = PyObject_GetAttrString(py_uuid, "UUID");
    std::ostringstream out;
    out << $1;
    PyObject *str = PyUnicode_DecodeUTF8(out.str().c_str(), out.str().size(), NULL);
    // Theoretically this string conversion could have just failed
    $result = PyObject_CallFunctionObjArgs(uuid_ctor, str, NULL);
    Py_DECREF(str);
}

我确实考虑过使用boost::python来简化一些代码,因为它会提升我们的包装。最后,我并没有为此烦恼。

你也可以使用boost lexical_cast来避免我使用过的字符串流。这主要是品味问题。

这些都不是超高性能代码,但是当你跨越语言边界时,它也不太可能是你系统中的瓶颈。您可以使用boost和Python的UUID模块提供的逐字节访问,并使其成为直接复制操作,但我避免了这一点,因为需要考虑字节顺序,这增加了复杂性。

注意:

  1. 没有正确的错误处理
  2. 在boost UUID输入类型上没有传递值的类型映射。
  3. 没有用于传递非const引用的类型映射。
  4. 使用此作为起点,所有这些都应该相当容易添加。

    这会按预期编译并运行:

    swig3.0 -c++ -python -py3 -Wall test.i
    g++ -g -std=c++1y -shared test_wrap.cxx -o _test.so -I/usr/include/python3.4 -Wall -Wextra -lpython3.4m 
    python3.4 run.py
    <class 'uuid.UUID'>
    b37c9285-f055-4f08-b4e9-4a238be3b09d
    <class 'uuid.UUID'>
    cf1071e6-2e7f-45af-8920-a68290ee61d4
    b37c9285-f055-4f08-b4e9-4a238be3b09d
    cf1071e6-2e7f-45af-8920-a68290ee61d4