1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
|
/*
* Copyright (c) 2003-2008, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of New Artisans LLC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Copyright 2004-2007 Roman Yakovenko.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef TUPLES_HPP_16_JAN_2007
#define TUPLES_HPP_16_JAN_2007
#include "boost/python.hpp"
#include "boost/tuple/tuple.hpp"
#include "boost/python/object.hpp" //len function
#include <boost/mpl/int.hpp>
#include <boost/mpl/next.hpp>
/**
* Converts boost::tuples::tuple<...> to\from Python tuple
*
* The conversion is done "on-the-fly", you should only register the conversion
* with your tuple classes.
* For example:
*
* typedef boost::tuples::tuple< int, double, std::string > triplet;
* boost::python::register_tuple< triplet >();
*
* That's all. After this point conversion to\from next types will be handled
* by Boost.Python library:
*
* triplet
* triplet& ( return type only )
* const triplet
* const triplet&
*
* Implementation description.
* The conversion uses Boost.Python custom r-value converters. r-value converters
* is very powerful and undocumented feature of the library. The only documentation
* we have is http://boost.org/libs/python/doc/v2/faq.html#custom_string .
*
* The conversion consists from two parts: "to" and "from".
*
* "To" conversion
* The "to" part is pretty easy and well documented ( http://docs.python.org/api/api.html ).
* You should use Python C API to create an instance of a class and than you
* initialize the relevant members of the instance.
*
* "From" conversion
* Lets start from analyzing one of the use case Boost.Python library have to
* deal with:
*
* void do_smth( const triplet& arg ){...}
*
* In order to allow calling this function from Python, the library should keep
* parameter "arg" alive until the function returns. In other words, the library
* should provide instances life-time management. The provided interface is not
* ideal and could be improved. You have to implement two functions:
*
* void* convertible( PyObject* obj )
* Checks whether the "obj" could be converted to an instance of the desired
* class. If true, the function should return "obj", otherwise NULL
*
* void construct( PyObject* obj, converter::rvalue_from_python_stage1_data* data)
* Constructs the instance of the desired class. This function will be called
* if and only if "convertible" function returned true. The first argument
* is Python object, which was passed as parameter to "convertible" function.
* The second object is some kind of memory allocator for one object. Basically
* it keeps a memory chunk. You will use the memory for object allocation.
*
* For some unclear for me reason, the library implements "C style Inheritance"
* ( http://www.embedded.com/97/fe29712.htm ). So, in order to create new
* object in the storage you have to cast to the "right" class:
*
* typedef converter::rvalue_from_python_storage<your_type_t> storage_t;
* storage_t* the_storage = reinterpret_cast<storage_t*>( data );
* void* memory_chunk = the_storage->storage.bytes;
*
* "memory_chunk" points to the memory, where the instance will be allocated.
*
* In order to create object at specific location, you should use placement new
* operator:
*
* your_type_t* instance = new (memory_chunk) your_type_t();
*
* Now, you can continue to initialize the instance.
*
* instance->set_xyz = read xyz from obj
*
* If "your_type_t" constructor requires some arguments, "read" the Python
* object before you call the constructor:
*
* xyz_type xyz = read xyz from obj
* your_type_t* instance = new (memory_chunk) your_type_t(xyz);
*
* Hint:
* In most case you don't really need\have to work with C Python API. Let
* Boost.Python library to do some work for you!
*
**/
namespace boost{ namespace python{
namespace details{
//Small helper function, introduced to allow short syntax for index incrementing
template< int index>
typename mpl::next< mpl::int_< index > >::type increment_index(){
typedef typename mpl::next< mpl::int_< index > >::type next_index_type;
return next_index_type();
}
}
template< class TTuple >
struct to_py_tuple{
typedef mpl::int_< tuples::length< TTuple >::value > length_type;
static PyObject* convert(const TTuple& c_tuple){
list values;
//add all c_tuple items to "values" list
convert_impl( c_tuple, values, mpl::int_< 0 >(), length_type() );
//create Python tuple from the list
return incref( python::tuple( values ).ptr() );
}
private:
template< int index, int length >
static void
convert_impl( const TTuple &c_tuple, list& values, mpl::int_< index >, mpl::int_< length > ) {
values.append( c_tuple.template get< index >() );
convert_impl( c_tuple, values, details::increment_index<index>(), length_type() );
}
template< int length >
static void
convert_impl( const TTuple&, list& values, mpl::int_< length >, mpl::int_< length >)
{}
};
template< class TTuple>
struct from_py_sequence{
typedef TTuple tuple_type;
typedef mpl::int_< tuples::length< TTuple >::value > length_type;
static void*
convertible(PyObject* py_obj){
if( !PySequence_Check( py_obj ) ){
return 0;
}
if( !PyObject_HasAttrString( py_obj, "__len__" ) ){
return 0;
}
python::object py_sequence( handle<>( borrowed( py_obj ) ) );
if( tuples::length< TTuple >::value != len( py_sequence ) ){
return 0;
}
if( convertible_impl( py_sequence, mpl::int_< 0 >(), length_type() ) ){
return py_obj;
}
else{
return 0;
}
}
static void
construct( PyObject* py_obj, converter::rvalue_from_python_stage1_data* data){
typedef converter::rvalue_from_python_storage<TTuple> storage_t;
storage_t* the_storage = reinterpret_cast<storage_t*>( data );
void* memory_chunk = the_storage->storage.bytes;
TTuple* c_tuple = new (memory_chunk) TTuple();
data->convertible = memory_chunk;
python::object py_sequence( handle<>( borrowed( py_obj ) ) );
construct_impl( py_sequence, *c_tuple, mpl::int_< 0 >(), length_type() );
}
static TTuple to_c_tuple( PyObject* py_obj ){
if( !convertible( py_obj ) ){
throw std::runtime_error( "Unable to construct boost::tuples::tuple from Python object!" );
}
TTuple c_tuple;
python::object py_sequence( handle<>( borrowed( py_obj ) ) );
construct_impl( py_sequence, c_tuple, mpl::int_< 0 >(), length_type() );
return c_tuple;
}
private:
template< int index, int length >
static bool
convertible_impl( const python::object& py_sequence, mpl::int_< index >, mpl::int_< length > ){
typedef typename tuples::element< index, TTuple>::type element_type;
object element = py_sequence[index];
extract<element_type> type_checker( element );
if( !type_checker.check() ){
return false;
}
else{
return convertible_impl( py_sequence, details::increment_index<index>(), length_type() );
}
}
template< int length >
static bool
convertible_impl( const python::object& py_sequence, mpl::int_< length >, mpl::int_< length > ){
return true;
}
template< int index, int length >
static void
construct_impl( const python::object& py_sequence, TTuple& c_tuple, mpl::int_< index >, mpl::int_< length > ){
typedef typename tuples::element< index, TTuple>::type element_type;
object element = py_sequence[index];
c_tuple.template get< index >() = extract<element_type>( element );
construct_impl( py_sequence, c_tuple, details::increment_index<index>(), length_type() );
}
template< int length >
static void
construct_impl( const python::object& py_sequence, TTuple& c_tuple, mpl::int_< length >, mpl::int_< length > )
{}
};
template< class TTuple>
void register_tuple(){
to_python_converter< TTuple, to_py_tuple<TTuple> >();
converter::registry::push_back( &from_py_sequence<TTuple>::convertible
, &from_py_sequence<TTuple>::construct
, type_id<TTuple>() );
};
} } //boost::python
#endif//TUPLES_HPP_16_JAN_2007
|