How-to
Building from source
Cowl can be built and run on Windows, macOS and Linux. It has also been successfully deployed to a wider range of platforms, including microcontroller units (such as Arduino and ST boards), with relatively minor build system setup. It can be compiled either as a static or dynamic library.
Requirements
In order to compile the library, you will need at a minimum:
There are additional requirements depending on which additional components you would like to build or compile (e.g. readers).
Functional reader:
Documentation:
Doxygen version 1.8 or later.
(optional) Sphinx version 2.0 or later, Breathe and the Read The Docs Theme.
Sphinx is optional as Doxygen will already generate some form of HTML docs, though not as fancy as the ones you are viewing.
Downloading the sources
You can find Cowl’s code on its git repository. Please note that it contains
submodules, so it is recommended that you clone it using the --recursive
flag.
git clone --recursive https://github.com/sisinflab-swot/cowl.git
Compiling
The following commands allow you to build Cowl:
# Generate the build system
cmake -B cmake-build -DCMAKE_BUILD_TYPE=Release
# [Optional] Edit build settings (build type, optimization options, etc.)
ccmake cmake-build
# Build the library
cmake --build cmake-build --config Release
# [Optional] Build the documentation
cmake --build cmake-build --target cowl-docs
# [Optional] Install the library and its headers in <install path>
cmake --install cmake-build --prefix <install path>
Linking
If you’re using CMake as your build system, you can link against Cowl by configuring your CMakeLists.txt file as follows:
# Assuming Cowl's source is under "lib/cowl"
add_subdirectory("lib/cowl" EXCLUDE_FROM_ALL)
target_link_libraries(your-target PRIVATE cowl)
For other build systems or if you are building directly through a compiler, please refer to their respective documentations. A relatively headache-free way to integrate Cowl involves compiling it and then linking against the built library, making sure the build system or compiler is aware of Cowl’s headers. Note that in this case you likely need to link against uLib as well.
Programming with Cowl
The easiest way to get started is by checking out the provided examples. However, in order to understand the principles behind the API, reading this section is strongly recommended.
API initialization
Before making any API call, you must invoke cowl_init()
, which is
needed in order to initialize the library’s internal state.
Calling API members without initializing the API is undefined behavior.
Ontology deserialization
In order to query an ontology you must first deserialize it, which can be done via
CowlManager
. Cowl can use multiple readers, either built-in or provided by the user.
For further information, refer to the related documentation.
OWL ontologies may import other ontologies, which may involve loading them from mass storage or retrieving them from the network. Cowl’s approach to imports reflects its focus on portability, so ontology retrieval is delegated to the end user.
Ontology queries
The core type of the API is CowlOntology
, which is essentially a collection
of CowlAxiom
instances. Under the hood, a CowlOntology
is an optimized
self-organizing in-memory store, which keeps axioms indexed by type and referenced entities,
allowing for very fast queries.
Ontology queries are functional, and query endpoints can be easily recognized in the
CowlOntology
API as they accept CowlIterator
instances.
See the related documentation and examples
for further information about how to use iterators.
Ontology editing and writing
Ontologies can be created from scratch, or existing ontologies can be edited by adding
or removing axioms, annotations and other constructs, as allowed by the CowlOntology
API.
Edited ontologies can then be written in any supported syntax
(see the related documentation).
Under the hood
This section illustrates a few important low-level details that you need to know in order to correctly use the library.
Memory management
Cowl uses reference counting for memory management.
Reference counts are increased and decreased via cowl_retain()
and cowl_release()
,
respectively. Generally speaking, each retain call must be balanced by a corresponding release,
in order to avoid leaking memory. There are also functions that return retained instances
to new or existing objects, which you must also make sure to release.
Functions that return retained instances are marked with the COWL_RETAINED
attribute
in the header files, and are annotated as such in the API documentation.
If a function returns a pointer to a Cowl object, and COWL_RETAINED
is not specified
in its declaration, then the returned instance is not retained, meaning its lifetime
is generally tied to that of some other object. If you need to keep it alive
after its owner has been deallocated, you must call cowl_retain()
on it.
Pseudo-inheritance
Since the OWL 2 specification is highly hierarchical, the API makes extensive use
of pseudo-inheritance for structs. Every data structure pseudo-inherits from CowlObject
,
whose concrete type can be queried via cowl_get_type()
.
Pseudo-inheritance allows you, as an example, to cast a CowlClass
to CowlClsExp
or CowlObject
and back. Of course, if the API returns a base pseudo-class
such as CowlClsExp
or CowlObject
, and you are unsure about its concrete
subclass, you can check its type via get_type
functions (e.g. cowl_cls_exp_get_type()
)
and cast accordingly. The API docs for type enumerations explicitly state the concrete type
associated with every enumeration value.