===============================
pygccxml.declarations query API
===============================

.. contents:: Table of contents

------------
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:

* ``scopedef_t`` - base class for all classes, that can contain other declarations

* ``namespace_t`` - derives from ``scopedef_t`` class, represents C++ namespace

* ``class_t`` - derives from ``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 - `Py++`_.
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 `Py++`_ 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
.. _`pygccxml`: ./pygccxml.html
.. _`Py++`: ./../pyplusplus/pyplusplus.html
.. _`SourceForge`: http://sourceforge.net/index.php
.. _`Python`: http://www.python.org
.. _`GCC-XML`: http://www.gccxml.org
.. _`UML diagram` : ./declarations_uml.png
.. _`parser package UML diagram` : ./parser_uml.png
.. _`ReleaseForge` : http://releaseforge.sourceforge.net
.. _`boost::type_traits` : http://www.boost.org/libs/type_traits/index.html
