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_t
class, represents C++ namespaceclass_t
- derives fromscopedef_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:
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
orFalse
.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 isint
.
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
isTrue
, then member function will be also searched within internal declarations.If
recursive
isFalse
, then member function will be searched only within current scope.What happen if
recursive
isNone
? Well.scopedef_t
class definesRECURSIVE_DEFAULT
variable. Its initial value isTrue
. So, if you don’t passrecursive
argument, the value ofRECURSIVE_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
isFalse
, then exceptionRuntimeError( "Multi declaration query returned 0 declarations." )
will be raised
allow_empty
uses 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_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 childrenscopedef_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 childrenscopedef_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.