Doxyparser

Ruby Gem for parsing C++ header files.

This library is based on Nokogiri (http://nokogiri.org) and takes as input the xml files generated by Doxygen (www.doxygen.org).

Using Doxygen allows us to parse even a set of non-compilable include files. This is very useful in case you need to parse only a subset of a big library which won't normally compile because of being incomplete or needing further build configuration (e.g Makefiles, VS, SCons, etc).

By using other tools which rely on a real C/C++ processor like gccxml or swig, you would normally get lots of compilation-related errors (which is undesired because we don't want to compile anything!!). Doxyparser is, in such cases, the lean alternative.

Install

gem install doxyparser

or add the following line to Gemfile:

gem 'doxyparser'

and run bundle install from your shell.

In order to use Doxyparser you need to first install Doxygen (www.doxygen.org) in your computer and Nokogiri (http://nokogiri.org)

Generating intermediate XML representation

Generate Doxygen documentation for a set of header files:

Doxyparser::gen_xml_docs('/path/to/c++/files', '/desired/path/to/doxygen/docs')

The first argument is a directory where the target c++ files are (normally .h header files) The second argument is a directory where the doxygen generated documentation will be created. It will contain afterwards one or two subdirectories: 'xml' and 'html' (according to the value of the gen_html flag)

There are other three optional arguments you can use. You have access to the console output too.

output = Doxyparser::gen_xml_docs('/path/to/c++/files', '/desired/path/to/doxygen/docs', read_recursively?, ['path/to/system/include/dirs'], gen_html?)

If you already have the needed XMLs, you can skip this step. Keep in mind that the EXTRACT_ALL = true flag in the Doxyfile must be set in order to accurately parse the files.

Parsing intermediate XMLs

Doxyparser uses Nokogiri under the hood to parse and query the Doxygen generated XMLs. Therefore we need the path to the directory containing those files, as generated with the former command. For instance:

xml_dir= '/desired/path/to/doxygen/docs/xml'

Doxygen generates a XML file for each namespace, class, struct and file in the given directory. Now we can parse them:

namespace = Doxyparser::parse_namespace("MyNamespace", xml_dir) # Instance of Doxyparser::Namespace
clazz = Doxyparser::parse_class("MyNamespace::MyClass", xml_dir) # Instance of Doxyparser::Class
struct = Doxyparser::parse_struct("MyNamespace::MyClass::InnerStruct", xml_dir) # Instance of Doxyparser::Struct
hfile = Doxyparser::parse_file("test.h", xml_dir) # Instance of Doxyparser::HFile

Each of them will have following attributes:

struct.name # == 'MyNamespace::MyClass::InnerStruct'
struct.basename # == 'InnerStruct'
struct.dir # == xml_dir == '/desired/path/to/doxygen/docs/xml'
struct.xml_path # == '/desired/path/to/doxygen/docs/xml/structMyNamespace_1_1MyClass_1_1InnerStruct.xml'

Querying Members

Namespaces

namespace_classes = namespace.classes # List of Doxyparser::Class namespace_structs = namespace.structs # List of Doxyparser::Struct inner_namespaces = namespace.innernamespaces # List of Doxyparser::Namespace namepace_enums = namespace.enums # List of Doxyparser::Enum namespace_variables = namespace.variables # List of Doxyparser::Variable namespace_functions = namespace.functions # List of Doxyparser::Function namespace_typedefs = namespace.typedefs # List of Doxyparser::Typedef

...and:

namespace_classes[0].parent == namespace # true

This last also applies for every other member (functions, enums, etc)

If you only want to query a subset of the namespace members, provide a list of strings or regexes as a parameter:

expected_functions=['operator>', 'operator<=', 'intersect', 'advanceRawPointer', 'any_cast']
ns_functions = @namespace.functions(expected_functions)

Files

file_classes = file.classes         # List of Doxyparser::Class
file_structs = file.structs         # List of Doxyparser::Struct
namepace_enums = file.enums             # List of Doxyparser::Enum
file_variables = file.variables         # List of Doxyparser::Variable
file_functions = file.functions         # List of Doxyparser::Function
file_typedefs = file.typedefs           # List of Doxyparser::Typedef

Information about other header files included by this:

list_files_included =  file.list_included   #  e.g ['cstdlib', 'list', 'map', 'subdir/test1.h']
files_included = file.files_included        # List of Doxyparser::HFile (only for local header files i.e with XML representation in xml_dir)

Information about other header files which include this one:

list_files_including =  file.list_including   #  e.g ['test3.h', 'test2.h', 'test4.h', 'subdir/test2.h']
files_including = file.files_including        # List of Doxyparser::HFile (only for local header files i.e with XML representation in xml_dir)

Classes and Structs

struct_def_file = struct.file                           # Instance of Doxyparser::HFile
struct_inner_classes = struct.innerclasses  :public     # List of Doxyparser::Class
struct_inner_structs = struct.innerstructs :protected   # List of Doxyparser::Struct
struct_enums = struct.innerenums :public                # List of Doxyparser::Enum
struct_attributes = struct.attributes(:public, :static) # List of Doxyparser::Variable
struct_methods = struct.methods(:private, :static)      # List of Doxyparser::Function
friend_members = struct.friends                         # List of Doxyparser::Friend
type_parameters = struct.template_params                # List of Doxyparser::Param
type_defs = struct.typedefs :public                     # List of Doxyparser::Typedef

...and as expected for every member:

struct_methods[0].parent == struct # true

The access modifier parameters (:public, :protected, :private) are optional. When none provided then :public is used. For methods and attributes :static can also be specified. When none provided then nil is used.

Like for namespaces, you can also specify an additional list parameter to query a subset of class/struct members.

# Note that the first two params need to be explicitly provided and we use nil instead of false
struct_attr = struct.attributes(:protected, nil, ['attribute1', 'attribute2'])

Functions/Methods, Variables/Attributes, Typedefs and Enums

  • General Properties:
puts myMethod.name          #  myMethod
puts myMethod.parent.name   #  MyNamespace::Class1
puts myMethod.location      #  /path/to/included/file/myIncludeFile.h:245
puts myMethod.definition    #  virtual void MyNamespace::Class1::myMethod
puts myMethod.args          #  (const Any &anything)
  • Functions and Methods only:

Consider for example the following definition:

const String & myFunction(int myInt, std:map<String &, std:list<unsigned char, int *>> myMap = null)

After parsing with doxyparser we obtain:

function_params = myFunction.params             # List of Doxyparser::Param
first_param_type = function_params[0].type      # Instance of Doxyparser::Type
puts first_param_type.name                      # int
puts first_param_type.declname                  # myInt
second_param_type = function_params[1].type     # Instance of Doxyparser::Type
puts second_param_type.name                     # std:map<String &, std:list<unsigned char, int * > > (full templates support)
puts second_param_type.declname                 # myMap
puts second_param_type.value                    # null
return_type = function.type                     # Instance of Doxyparser::Type
puts return_type.name                           # const String &
  • Methods only:
MyClass(char * xc);
puts myMethod.name                  # > MyClass 
puts myMethod.constructor?          # > true
puts myMethod.destructor?           # > false
~MyClass();
puts myMethod.name                  # > ~MyClass    
puts myMethod.constructor?          # > false
puts myMethod.destructor?           # > true
MyClass* getProp();
MyClass* get_prop();
MyClass* GetProp();
puts myMethod.name                  # > getProp/get_prop/GetProp    
puts myMethod.getter_for            # > prop    
puts myMethod.setter_for            # > nil
void setProp(MyClass *obj);
void set_prop(MyClass *obj);
void isProp(MyClass *obj);
puts myMethod.name                  # > setProp/set_prop/isProp
puts myMethod.setter_for            # > prop
puts myMethod.getter_for            # > nil
  • Enums only:
enum MyEnum{
    A, B, C
};
puts myEnum.name                # > MyEnum
puts myEnum.values.join(", ")   # > A, B, C

...and anonymous enums:

enum {
    A, B, C
};
puts myEnum.name                # > _Enum
puts myEnum.values.join(", ")   # > A, B, C