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.

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

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:

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:

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.

    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.

    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.

    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.

    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:

struct A{
    A* clone() const { return new A(); }
    ...
};
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:

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

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:

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.