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:
scopedef_t- base class for all classes, that can contain other declarationsnamespace_t- derives fromscopedef_tclass, represents C++ namespaceclass_t- derives fromscopedef_tclass, 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:
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:
namePython 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' )
functionPython 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
TrueorFalse.impls = my_class.member_functions( lambda decl: decl.name.endswith( 'impl' ) )
implswill contain all member functions, that their name ends with “impl”.
return_typethe 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_typesPython 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_funcswill contain all member functions, which have two arguments and type of second argument isint.
header_dirPython 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_filePython 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' )
recursivePython boolean object.
If
recursiveisTrue, then member function will be also searched within internal declarations.If
recursiveisFalse, then member function will be searched only within current scope.What happen if
recursiveisNone? Well.scopedef_tclass definesRECURSIVE_DEFAULTvariable. Its initial value isTrue. So, if you don’t passrecursiveargument, the value ofRECURSIVE_DEFAULTvariable will be used. This “yet another level of indirection” allows you to configure pygccxml “select” functions in one place for all project.
allow_emptyPython boolean object, it says pygccxml what to do if query returns empty.
If
allow_emptyisFalse, then exceptionRuntimeError( "Multi declaration query returned 0 declarations." )will be raised
allow_emptyuses same technique asrecursive, to allow you to customize the behavior project-wise. The relevant class variable name isALLOW_EMPTY_MDECL_WRAPPER. Its initial value isFalse.
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_nrPython dictionary, that contains mapping between declaration type and declarations in the current scope.
scopedef_t.type2decls_nrcontains only declaration from the current scope.scopedef_t.type2declscontains declarations from the current scope and its childrenscopedef_t._type2name2decls,scopedef_t._type2name2decls_nrPython dictionary, that contains mapping between declaration type and another dictionary. This second dictionary contains mapping between a declaration name and declaration.
scopedef_t.type2name2decls_nrcontains only declaration from the current scope.scopedef_t.type2name2declscontains declarations from the current scope and its childrenscopedef_t._all_declsA 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.