1 // Copyright David Abrahams 2001.
2 // Distributed under the Boost Software License, Version 1.0. (See
3 // accompanying file LICENSE_1_0.txt or copy at
4 // http://www.boost.org/LICENSE_1_0.txt)
5 #include <boost/python/converter/registry.hpp>
6 #include <boost/python/converter/registrations.hpp>
7 #include <boost/python/converter/builtin_converters.hpp>
8
9 #include <set>
10 #include <stdexcept>
11
12 #if defined(__APPLE__) && defined(__MACH__) && defined(__GNUC__) \
13 && __GNUC__ == 3 && __GNUC_MINOR__ <= 4 && !defined(__APPLE_CC__)
14 # define BOOST_PYTHON_CONVERTER_REGISTRY_APPLE_MACH_WORKAROUND
15 #endif
16
17 #if defined(BOOST_PYTHON_TRACE_REGISTRY) \
18 || defined(BOOST_PYTHON_CONVERTER_REGISTRY_APPLE_MACH_WORKAROUND)
19 # include <iostream>
20 #endif
21
22 namespace boost { namespace python { namespace converter {
expected_from_python_type() const23 BOOST_PYTHON_DECL PyTypeObject const* registration::expected_from_python_type() const
24 {
25 if (this->m_class_object != 0)
26 return this->m_class_object;
27
28 std::set<PyTypeObject const*> pool;
29
30 for(rvalue_from_python_chain* r = rvalue_chain; r ; r=r->next)
31 if(r->expected_pytype)
32 pool.insert(r->expected_pytype());
33
34 //for now I skip the search for common base
35 if (pool.size()==1)
36 return *pool.begin();
37
38 return 0;
39
40 }
41
to_python_target_type() const42 BOOST_PYTHON_DECL PyTypeObject const* registration::to_python_target_type() const
43 {
44 if (this->m_class_object != 0)
45 return this->m_class_object;
46
47 if (this->m_to_python_target_type != 0)
48 return this->m_to_python_target_type();
49
50 return 0;
51 }
52
get_class_object() const53 BOOST_PYTHON_DECL PyTypeObject* registration::get_class_object() const
54 {
55 if (this->m_class_object == 0)
56 {
57 ::PyErr_Format(
58 PyExc_TypeError
59 , const_cast<char*>("No Python class registered for C++ class %s")
60 , this->target_type.name());
61
62 throw_error_already_set();
63 }
64
65 return this->m_class_object;
66 }
67
to_python(void const volatile * source) const68 BOOST_PYTHON_DECL PyObject* registration::to_python(void const volatile* source) const
69 {
70 if (this->m_to_python == 0)
71 {
72 handle<> msg(
73 #if PY_VERSION_HEX >= 0x3000000
74 ::PyUnicode_FromFormat
75 #else
76 ::PyString_FromFormat
77 #endif
78 (
79 "No to_python (by-value) converter found for C++ type: %s"
80 , this->target_type.name()
81 )
82 );
83
84 PyErr_SetObject(PyExc_TypeError, msg.get());
85
86 throw_error_already_set();
87 }
88
89 return source == 0
90 ? incref(Py_None)
91 : this->m_to_python(const_cast<void*>(source));
92 }
93
94 namespace
95 {
96 template< typename T >
delete_node(T * node)97 void delete_node( T* node )
98 {
99 if( !!node && !!node->next )
100 delete_node( node->next );
101 delete node;
102 }
103 }
104
~registration()105 registration::~registration()
106 {
107 delete_node(lvalue_chain);
108 delete_node(rvalue_chain);
109 }
110
111
112 namespace // <unnamed>
113 {
114 typedef registration entry;
115
116 typedef std::set<entry> registry_t;
117
118 #ifndef BOOST_PYTHON_CONVERTER_REGISTRY_APPLE_MACH_WORKAROUND
entries()119 registry_t& entries()
120 {
121 static registry_t registry;
122
123 # ifndef BOOST_PYTHON_SUPPRESS_REGISTRY_INITIALIZATION
124 static bool builtin_converters_initialized = false;
125 if (!builtin_converters_initialized)
126 {
127 // Make this true early because registering the builtin
128 // converters will cause recursion.
129 builtin_converters_initialized = true;
130
131 initialize_builtin_converters();
132 }
133 # ifdef BOOST_PYTHON_TRACE_REGISTRY
134 std::cout << "registry: ";
135 for (registry_t::iterator p = registry.begin(); p != registry.end(); ++p)
136 {
137 std::cout << p->target_type << "; ";
138 }
139 std::cout << '\n';
140 # endif
141 # endif
142 return registry;
143 }
144 #else
static_registry()145 registry_t& static_registry()
146 {
147 static registry_t result;
148 return result;
149 }
150
static_builtin_converters_initialized()151 bool static_builtin_converters_initialized()
152 {
153 static bool result = false;
154 if (result == false) {
155 result = true;
156 std::cout << std::flush;
157 return false;
158 }
159 return true;
160 }
161
entries()162 registry_t& entries()
163 {
164 # ifndef BOOST_PYTHON_SUPPRESS_REGISTRY_INITIALIZATION
165 if (!static_builtin_converters_initialized())
166 {
167 initialize_builtin_converters();
168 }
169 # ifdef BOOST_PYTHON_TRACE_REGISTRY
170 std::cout << "registry: ";
171 for (registry_t::iterator p = static_registry().begin(); p != static_registry().end(); ++p)
172 {
173 std::cout << p->target_type << "; ";
174 }
175 std::cout << '\n';
176 # endif
177 # endif
178 return static_registry();
179 }
180 #endif // BOOST_PYTHON_CONVERTER_REGISTRY_APPLE_MACH_WORKAROUND
181
get(type_info type,bool is_shared_ptr=false)182 entry* get(type_info type, bool is_shared_ptr = false)
183 {
184 # ifdef BOOST_PYTHON_TRACE_REGISTRY
185 registry_t::iterator p = entries().find(entry(type));
186
187 std::cout << "looking up " << type << ": "
188 << (p == entries().end() || p->target_type != type
189 ? "...NOT found\n" : "...found\n");
190 # endif
191 std::pair<registry_t::const_iterator,bool> pos_ins
192 = entries().insert(entry(type,is_shared_ptr));
193
194 # if __MWERKS__ >= 0x3000
195 // do a little invariant checking if a change was made
196 if ( pos_ins.second )
197 assert(entries().invariants());
198 # endif
199 return const_cast<entry*>(&*pos_ins.first);
200 }
201 } // namespace <unnamed>
202
203 namespace registry
204 {
insert(to_python_function_t f,type_info source_t,PyTypeObject const * (* to_python_target_type)())205 void insert(to_python_function_t f, type_info source_t, PyTypeObject const* (*to_python_target_type)())
206 {
207 # ifdef BOOST_PYTHON_TRACE_REGISTRY
208 std::cout << "inserting to_python " << source_t << "\n";
209 # endif
210 entry* slot = get(source_t);
211
212 assert(slot->m_to_python == 0); // we have a problem otherwise
213 if (slot->m_to_python != 0)
214 {
215 std::string msg = (
216 std::string("to-Python converter for ")
217 + source_t.name()
218 + " already registered; second conversion method ignored."
219 );
220
221 if ( ::PyErr_Warn( NULL, const_cast<char*>(msg.c_str()) ) )
222 {
223 throw_error_already_set();
224 }
225 }
226 slot->m_to_python = f;
227 slot->m_to_python_target_type = to_python_target_type;
228 }
229
230 // Insert an lvalue from_python converter
insert(convertible_function convert,type_info key,PyTypeObject const * (* exp_pytype)())231 void insert(convertible_function convert, type_info key, PyTypeObject const* (*exp_pytype)())
232 {
233 # ifdef BOOST_PYTHON_TRACE_REGISTRY
234 std::cout << "inserting lvalue from_python " << key << "\n";
235 # endif
236 entry* found = get(key);
237 lvalue_from_python_chain *registration = new lvalue_from_python_chain;
238 registration->convert = convert;
239 registration->next = found->lvalue_chain;
240 found->lvalue_chain = registration;
241
242 insert(convert, 0, key,exp_pytype);
243 }
244
245 // Insert an rvalue from_python converter
insert(convertible_function convertible,constructor_function construct,type_info key,PyTypeObject const * (* exp_pytype)())246 void insert(convertible_function convertible
247 , constructor_function construct
248 , type_info key
249 , PyTypeObject const* (*exp_pytype)())
250 {
251 # ifdef BOOST_PYTHON_TRACE_REGISTRY
252 std::cout << "inserting rvalue from_python " << key << "\n";
253 # endif
254 entry* found = get(key);
255 rvalue_from_python_chain *registration = new rvalue_from_python_chain;
256 registration->convertible = convertible;
257 registration->construct = construct;
258 registration->expected_pytype = exp_pytype;
259 registration->next = found->rvalue_chain;
260 found->rvalue_chain = registration;
261 }
262
263 // Insert an rvalue from_python converter
push_back(convertible_function convertible,constructor_function construct,type_info key,PyTypeObject const * (* exp_pytype)())264 void push_back(convertible_function convertible
265 , constructor_function construct
266 , type_info key
267 , PyTypeObject const* (*exp_pytype)())
268 {
269 # ifdef BOOST_PYTHON_TRACE_REGISTRY
270 std::cout << "push_back rvalue from_python " << key << "\n";
271 # endif
272 rvalue_from_python_chain** found = &get(key)->rvalue_chain;
273 while (*found != 0)
274 found = &(*found)->next;
275
276 rvalue_from_python_chain *registration = new rvalue_from_python_chain;
277 registration->convertible = convertible;
278 registration->construct = construct;
279 registration->expected_pytype = exp_pytype;
280 registration->next = 0;
281 *found = registration;
282 }
283
lookup(type_info key)284 registration const& lookup(type_info key)
285 {
286 return *get(key);
287 }
288
lookup_shared_ptr(type_info key)289 registration const& lookup_shared_ptr(type_info key)
290 {
291 return *get(key, true);
292 }
293
query(type_info type)294 registration const* query(type_info type)
295 {
296 registry_t::iterator p = entries().find(entry(type));
297 # ifdef BOOST_PYTHON_TRACE_REGISTRY
298 std::cout << "querying " << type
299 << (p == entries().end() || p->target_type != type
300 ? "...NOT found\n" : "...found\n");
301 # endif
302 return (p == entries().end() || p->target_type != type) ? 0 : &*p;
303 }
304 } // namespace registry
305
306 }}} // namespace boost::python::converter
307