2012-02-19 116 views
2

我想创建一个从C++结构到PyLong的类型映射。swig typemap从C++ struct到PyLong

例如,我有以下表示128位数的结构,我想在Python接口中将它作为一个简单的Python无符号long来访问。

struct my_128 { 
    u_int64_t raw[2]; 
}; 

我该如何创建这样一个typemap?

回答

2

缺席全部的差错检验,这些typemaps工作:

%typemap(in) struct my_128 { 
    PyObject* temp; 
    PyObject* shift; 
    if(!PyLong_Check($input) && !PyInt_Check($input)) 
    { 
     PyErr_SetString(PyExc_TypeError,"Must be int or long type"); 
     return NULL; 
    } 
    $1.raw[0] = PyInt_AsUnsignedLongLongMask($input); // low 64-bits 
    shift = PyInt_FromLong(64); 
    temp = PyNumber_Rshift($input,shift); 
    $1.raw[1] = PyInt_AsUnsignedLongLongMask(temp); // high 64-bits 
    Py_DECREF(temp); 
    Py_DECREF(shift); 
} 

%typemap(out) struct my_128 { 
    PyObject* low; 
    PyObject* high; 
    PyObject* shift; 
    PyObject* intermediate; 
    low = PyLong_FromUnsignedLongLong($1.raw[0]); 
    high = PyLong_FromUnsignedLongLong($1.raw[1]); 
    shift = PyInt_FromLong(64); 
    intermediate = PyNumber_Lshift(high,shift); 
    $result = PyNumber_Add(low,intermediate); 
    Py_DECREF(low); 
    Py_DECREF(high); 
    Py_DECREF(intermediate); 
    Py_DECREF(shift); 
} 
+0

+1,这远比我的解决方案更好 - 我错过了'PyNumber'功能 – Flexo 2012-02-22 09:22:50

0

我有一些工作,但它不漂亮。问题是它似乎是从C制作PyLong的唯一方法,它比long longis with a string更长!

因此,对于类型表是把你的my_128,并公开其作为PyLong当它从一个函数返回,你可以这样做:

%typemap(out) my_128 { 
    std::ostringstream s; 
    s << "0x" 
    << std::setfill('0') << std::setw(8) << std::hex << $1.raw[0] 
    << std::setfill('0') << std::setw(8) << std::hex << $1.raw[1]; 
    char *c = strdupa(s.str().c_str()); // Avoids a const cast without leaking ever 
    $result = PyLong_FromString(c,0,0); 
} 

它打印其十六进制为stringstream,然后从构建一个PyLong 。

另一种方式的相应类型映射甚至更丑陋。我们需要通过调用内置的hex()来让我们的PyLong和说服python把它转换成合适的字符串。

然后,我们需要将它按摩到我们可以从(两个)stringstreams(否则第一个窃取所有输入)中读取的东西。这最终看起来像:

%typemap(in) my_128 { 
    PyObject *moduleName = PyString_FromString((char*)"__builtin__"); 
    assert(moduleName); 
    PyObject *module = PyImport_Import(moduleName); 
    assert(module); 

    PyObject *hex = PyObject_GetAttrString(module,(char*)"hex"); 
    assert(hex); 
    PyObject *args = PyTuple_Pack(1,$input); 
    assert(args); 
    PyObject *result = PyObject_CallObject(hex, args); 
    assert(result); 
    std::string str(PyString_AsString(result)); 
    if (str.find("0x")!=std::string::npos) { 
    str=str.substr(2); 
    } 
    if (str.find("L") != std::string::npos) { 
    str=str.substr(0,str.size()-1); 
    } 
    assert(str.size()); 
    if (str.size() > 16) { 
    PyErr_SetString(PyExc_ValueError, "Expected at most a 128-bit int"); 
    return NULL; 
    } 
    std::istringstream s1(str.substr(0,8)); 
    if (!(s1 >> std::hex >> $1.raw[0])) { 
     $1.raw[0]=0; 
    } 
    std::istringstream s2(str.substr(8,16)); 
    if (!(s2 >> std::hex >> $1.raw[1])) { 
     $1.raw[1]=0; 
    } 
    // TODO: check that these really worked! 
} 

这可能会使用一堆更多的错误处理仍然。

%module test 

%{ 
#include <sstream> 
#include <iomanip> 
#include <string.h> 
#include <iostream> //for testing 
%} 

// Typemaps go here 

%inline { 
struct my_128 { 
    u_int64_t raw[2]; 
}; 

my_128 create() { 
    const my_128 r = {0xdeadbeef, 0xdeadbeef}; 
    return r; 
} 

void display (my_128 in) { 
    std::cout << std::setfill('0') << std::setw(8) << std::hex << in.raw[0] 
      << std::setfill('0') << std::setw(8) << std::hex << in.raw[1] << std::endl; 
} 
} 

从快速初始测试为:

 
Python 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24) 
[GCC 4.5.2] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import test 
>>> test.create() 
16045690984833335023L 
>>> test.display(16045690984833335023L) 
deadbeefdeadbeef 
>>> test.display(test.create()) 
deadbeefdeadbeef 
>>> test.display(test.create()+1) 
deadbeefdeadbef0 
>>> test.display(test.create()+100) 
deadbeefdeadbf53 
>>> test.display(test.create()+10000000) 
deadbeefdf46556f 
>>> test.display(test.create()+100000000000000000000) 
Traceback (most recent call last): 
    File "", line 1, in 
ValueError: Expected at most a 128-bit int 
>>> test.display(test.create()+100000000000000000) 
e01104683c37beef 
>>> test.display(test.create()+10000000000000) 
deadc8082d205eef 
>>> 

虽然它尚未办理“短”整数正确,因为substr()电话

我测试过这些。