• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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