File: query_interface.rst

package info (click to toggle)
pygccxml 3.0.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 8,444 kB
  • sloc: xml: 29,841; python: 13,914; cpp: 2,671; makefile: 163; ansic: 59
file content (298 lines) | stat: -rw-r--r-- 10,702 bytes parent folder | download | duplicates (6)
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
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
======================
Declarations query API
======================

------------
Introduction
------------
You parsed the source files. Now you have to do some real work with the extracted
information, right? pygccxml provides very powerful and simple interface to
query about extracted declarations.

Just an example. I want to select all member functions, which have 2 arguments.
I don't care about first argument type, but I do want second argument type to be
a reference to an integer. More over, I want those functions names to end with
"impl" string and they should be protected or private.

.. code-block:: python

  #global_ns is the reference to an instance of namespace_t object, that
  #represents global namespace
  query = declarations.custom_matcher_t( lambda mem_fun: mem_fun.name.endswith( 'impl' )
  query = query & ~declarations.access_type_matcher_t( 'public' )
  global_ns.member_functions( function=query, arg_types=[None, 'int &'] )

The example is complex, but still readable. In many cases you will find
yourself, looking for one or many declarations, using one or two declaration properties.
For example:

.. code-block:: python

  global_ns.namespaces( 'details' )

This call will return all namespaces with name 'details'.

--------------
User interface
--------------

As you already know, ``pygccxml.declarations`` package defines the following classes:

* :class:`scopedef_t <pygccxml.declarations.scopedef.scopedef_t>` - base class
  for all classes, that can contain other declarations

* :class:`namespace_t  <pygccxml.declarations.namespace.namespace_t>` - derives
  from :class:`scopedef_t <pygccxml.declarations.scopedef.scopedef_t>` class,
  represents C++ namespace

* :class:`class_t  <pygccxml.declarations.class_declaration.class_t>` - derives
  from :class:`scopedef_t <pygccxml.declarations.scopedef.scopedef_t>` class,
  represents C++ class/struct/union.

So, the query methods defined on ``scopedef_t`` class could be used on instances
of ``class_t`` and ``namespace_t`` classes. I am sure you knew that.

Usage examples
--------------

I will explain the usage of ``member_function`` and ``member_functions`` methods.
The usage of other methods is very similar to them. Here is definition of those
methods:

.. code-block:: python

  def member_function(  self,
                        name=None,
                        function=None,
                        return_type=None,
                        arg_types=None,
                        header_dir=None,
                        header_file=None,
                        recursive=None )

  mem_fun = member_function #just an alias

  def member_functions( self,
                        name=None,
                        function=None,
                        return_type=None,
                        arg_types=None,
                        header_dir=None,
                        header_file=None,
                        recursive=None,
                        allow_empty=None )
  mem_funs = member_functions


As you can see, from the method arguments you can search for member function
by:

  * ``name``

    Python string, that contains member function name or full name.

    .. code-block:: python

      do_smth = my_class.member_function( 'do_smth' )
      do_smth = my_class.member_function( 'my_namespace::my_class::do_smth' )

  * ``function``

    Python callable object. You would use this functionality, if you need to
    build custom query. This object will be called with one argument - declaration,
    and it should return ``True`` or ``False``.

    .. code-block:: python

      impls = my_class.member_functions( lambda decl: decl.name.endswith( 'impl' ) )


    ``impls`` will contain all member functions, that their name ends with "impl".

  * ``return_type``

    the function return type. This argument can be string or an object that describes
    C++ type.

    .. code-block:: python

      mem_funcs = my_class.member_functions( return_type='int' )

      i = declarations.int_t()
      ref_i = declarations.reference_t( i )
      const_ref_i = declarations.const_t( ref_i )
      mem_funcs = my_class.member_functions( return_type=const_ref_int )

  * ``arg_types``

    Python list that contains description of member function argument types.
    This list could be a mix of Python strings and objects that describes C++
    type. Size of list says how many arguments function should have. If you want
    to skip some argument type from within comparison, you put ``None``, into
    relevant position within the list.

    .. code-block:: python

      mem_funcs = my_class.member_functions( arg_types=[ None, 'int'] )

    ``mem_funcs`` will contain all member functions, which have two arguments
    and type of second argument is ``int``.

  * ``header_dir``

    Python string, that contains full path to directory, which contains file,
    which contains the function declaration

    ``mem_funcs = my_namespace.member_functions( header_dir='/home/roman/xyz' )``

  * ``header_file``

    Python string, that contains full path to file, which contains the function
    declaration.

    ``mem_funcs = my_namespace.member_functions( header_dir='/home/roman/xyz/xyz.hpp' )``

  * ``recursive``

    Python boolean object.

    If ``recursive`` is ``True``, then member function will be also searched
    within internal declarations.

    If ``recursive`` is ``False``, then member function will be searched only
    within current scope.

    What happen if ``recursive`` is ``None``? Well. ``scopedef_t`` class defines
    ``RECURSIVE_DEFAULT`` variable. Its initial value is ``True``. So, if you
    don't pass ``recursive`` argument, the value of ``RECURSIVE_DEFAULT`` variable
    will be used. This "yet another level of indirection" allows you to configure
    pygccxml "select" functions in one place for all project.

  * ``allow_empty``

    Python boolean object, it says pygccxml what to do if query returns empty.

    If ``allow_empty`` is ``False``, then exception
    ``RuntimeError( "Multi declaration query returned 0 declarations." )``
    will be raised

    ``allow_empty`` uses same technique as ``recursive``, to allow you to customize
    the behavior project-wise. The relevant class variable name is
    ``ALLOW_EMPTY_MDECL_WRAPPER``. Its initial value is ``False``.

Now, when you understand, how to call those functions, I will explain what they
return.

``member_function`` will always return reference to desired declaration. If
declaration could not be found or there are more then one declaration that
match query ``RuntimeError`` exception will be raised.

Return value of ``member_functions`` is not Python list or set, but instance
of ``mdecl_wrapper_t`` class. This class allows you to work on all selected
objects at once. I will give an example from another project - https://pypi.python.org/pypi/pyplusplus/.
In order to help `Boost.Python`_ to manage objects life time, all functions
should have `call policies`_. For example:

.. code-block:: c++

  struct A{
      A* clone() const { return new A(); }
      ...
  };

.. code-block:: c++

  struct B{
      B* clone() const { return new B(); }
      ...
  };

``clone`` member function `call policies`_ is ``return_value_policy<manage_new_object>()``.
The following code applies the `call policies`_ on all ``clone`` member functions within the
project:

.. code-block:: python

  #global_ns - instance of namespace_t class, that contains reference to global namespace
  clone = global_ns.member_functions( 'clone' )
  clone.call_policies = return_value_policy( manage_new_object )


Another example, from https://pypi.python.org/pypi/pyplusplus/ project. Sometimes it is desirable to
exclude declaration, from being exported to Python. The following code will exclude
``clone`` member function from being exported:

.. code-block:: python

  global_ns.member_functions( 'clone' ).exclude()

As you can see this class allows you to write less code. Basically using this
class you don't have to write loops. If will do it for you. Also if you insist to
write loops, ``mdecl_wrapper_t`` class implements ``__len__``, ``__getitem__``
and ``__iter__`` methods. So you can write the following code:

.. code-block:: python

  for clone in global_ns.member_functions( 'clone' ):
      print clone.parent.name


----------------------
Implementation details
----------------------

Performance
-----------

For big projects, performance is critical. When you finished to build/change
declarations tree, then you can call ``scopedef_t.init_optimizer`` method.
This method will initialize few data structures, that will help to minimize the
number of compared declarations. The price you are going to pay is memory usage.

Data structures
~~~~~~~~~~~~~~~
Here is a short explanation of what data structures is initialized.

* ``scopedef_t._type2decls``, ``scopedef_t._type2decls_nr``

  Python dictionary, that contains mapping between declaration type and
  declarations in the current scope.

  ``scopedef_t.type2decls_nr`` contains only declaration from the current scope.
  ``scopedef_t.type2decls`` contains declarations from the current scope and its children

* ``scopedef_t._type2name2decls``, ``scopedef_t._type2name2decls_nr``

  Python dictionary, that contains mapping between declaration type and
  another dictionary. This second dictionary contains mapping between
  a declaration name and declaration.

  ``scopedef_t.type2name2decls_nr`` contains only declaration from the current scope.
  ``scopedef_t.type2name2decls`` contains declarations from the current scope and its children

* ``scopedef_t._all_decls``

  A flat list of all declarations, including declarations from the children scopes.

Except ``scopedef_t.decl`` and ``scopedef_t.decls`` methods, all other queries
have information about declaration type.

If you include ``name`` into your query, you will get the best performance.

----------------
More information
----------------

I think, I gave you the important information. If you need definition of some
query method, you can take a look on API documentation or into source code.


.. _`Boost.Python`: http://boost.org/libs/python/doc/tutorial/doc/html/index.html
.. _`call policies`: http://boost.org/libs/python/doc/tutorial/doc/html/python/functions.html#python.call_policies
.. _`Call policies`: http://boost.org/libs/python/doc/tutorial/doc/html/python/functions.html#python.call_policies

.. _`Python`: http://www.python.org
.. _`GCC-XML`: http://www.gccxml.org
.. _`UML diagram` : declarations_uml.png
.. _`parser package UML diagram` : parser_uml.png
.. _`boost::type_traits` : http://www.boost.org/libs/type_traits/index.html