У меня есть кое-что, что работает, но это не красиво. Проблема в том, что единственный способ сделать PyLong из C длиннее long long
- это строка !
Так что для карты типов, которая берет ваш 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 из этого.
Соответствующая карта типов, идущая в другую сторону, еще страшнее. Нам нужно взять наш PyLong и убедить Python преобразовать его в подходящую строку, вызвав для него встроенный hex()
.
Затем нам нужно втирать это во что-то, из чего мы можем прочитать (два) 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()
.