C++ tutorial

C++ tutorial

Quick start: A basic C++ program using the API

#include <string>
// This header file provides C++ specific templates to make working with the API
// easier.
#include "cellml-api-cxx-support.hpp"
// This is the standard C++ interface for the core CellML API.
#include "IfaceCellML_APISPEC.hxx"
// This is a C++ binding specific header that defines how to get the bootstrap
// object.
#include "CellMLBootstrap.hpp"
int
main(int argc, char** argv)
{
// Fetch a bootstrap object. The bootstrap object is a special 'root' object
// from which you can get at everything else. CreateCellMLBootstrap() is a
// C++ binding specific method, which fetches the CellMLBootstrap object.
// It is the only non-OO method that you should ever call from the CellML
// API.
// Note that RETURN_INTO_OBJREF is a macro from Utilities.hxx. It is creating
// a variable called cbs, and assigning into it. CreateCellMLBootstrap
// returns iface::cellml_api::CellMLBootstrap*. We could assign this directly
// into a variable. We would then have to call cbs->release_ref() to release
// the object when we have finished with it. The ObjRef<> template does this
// for us, so there is no chance we will forget to clean up on a certain
// exit path.
RETURN_INTO_OBJREF(cbs, iface::cellml_api::CellMLBootstrap,
CreateCellMLBootstrap());
// Now would be a good time to see what methods we can call. In the
// CellML_DOM_API source, find interfaces/CellML_APISPEC.idl.
// This defines the interfaces you can call. Search down to find
// this text...
/*
interface CellMLBootstrap
: XPCOM::IObject
{
...
*/
// We want to load a model, so we want the modelLoader attribute. We fetch
// the attribute like this...
RETURN_INTO_OBJREF(ml, iface::cellml_api::DOMModelLoader, cbs->modelLoader());
// Start a try, because we might get an exception...
try
{
// We now have a DOMModelLoader, stored in ml. DOMModelLoader inherits from
// ModelLoader, which defines a loadFromURL operation (check in the IDL).
// Be warned that is a synchronous (blocking) load. In a real application,
// you are probably better to download the file using another asynchronous
// http library, and then creating the model from the serialised text.
RETURN_INTO_OBJREF(model, iface::cellml_api::Model,
ml->loadFromURL(L"http://www.cellml.org/models/beeler_reuter_1977_version04/download"));
// Fetch the models cmeta:id (there obviously lots of other things we could
// do here!)
RETURN_INTO_WSTRING(cmid, model->cmetaId());
printf("Model's cmeta:id is %S\n", cmid.c_str());
}
// Most parts of the CellML API raise this exception. The DOM/MathML API, on the
// other hand, raises iface::dom::DOMException.
catch (iface::cellml_api::CellMLException&)
{
// Unfortunately, due to the need to support the 'lowest common
// denominator' of functionality in our bindings, exceptions can't have
// supplementary information (to suit XPCOM). However, many classes have
// a way to get the last error, e.g. lastErrorMessage on ModelLoader.
// However, threadsafety is potentially an issue with this.
RETURN_INTO_WSTRING(msg, ml->lastErrorMessage());
printf("Got a CellML Exception loading a model. Error was %S\n",
msg.c_str());
return 1;
}
return 0;
}

This can be compiled and run on a *nix system as follows (where $CELLML_SDK is the path to the SDK directory or your install prefix):

export CELLML_SDK=path/to/cellml-sdk
g++ -I$CELLML_SDK/include -L$CELLML_SDK/lib -lcellml ./MyTest.cpp -o MyTest
LD_LIBRARY_PATH=$CELLML_SDK/lib ./MyTest

Note: When linking on Windows with Visual C++, it is normal to see the following compiler warnings, due to the coding style of the CellML API: warning C4290 and warning C4091. Neither of these warnings represent a real problem with the API; you can suppress them by giving MSVC the "/wd4290" and "/wd4091" flags.

Concepts when using the API Reference Implementation from C++

When using the CellML API Reference Implementation from C++, the most basic way of dealing with an API object is as a pointer of type iface::namespace::interfaceName*. These classes are defined in headers named IfaceIDLNAME.hxx, where IDLNAME is the name of the IDL file for the service you want. Operations correspond to identically-named class methods on the class, and attributes correspond to a setter of the same name which takes the value to set to, and a getter, which takes no arguments and returns the value. Read-only attributes have no setter.

Because all interfaces inherit from iface::XPCOM::IObject, you can call add_ref to add a reference to an object, and release_ref to release a reference. When you retrieve a value from an attribute getter, an out parameter, or a return value, the API automatically increases its reference count, and you will need to call release_ref when you have finished with it (with one extra call for each time you call add_ref).

An object can support multiple interfaces, and sometimes you might have a pointer to an interface and want a pointer to an interface that inherits from that interface. Because the API is designed to work correctly with multiple layers of bridges between the caller and the implementation, you might have a pointer to a bridge object that doesn't support all the interfaces the object itself supoprts. Therefore, for robust and reliable code, you should never simply attempt to directly cast the object in this case. Instead, the query_interface operation is used. This takes a string, formatted as namespace::interfaceName, and returns a void* which can be cast with a reinterpret_cast to iface::namespace::interfaceName*.

Because managing reference counts manually in C++ is verbose and error prone, a C++ specific header containing some template classes is provided, called cellml-api-cxx-support.hpp. It provides a template ObjRef<T>; the type T is filled in with an interface type (not the pointer). For example, ObjRef<iface::XPCOM::IObject> is a pointer to an object implementing the IObject interface. Assigning a pointer into an ObjRef increases the reference count, and when the ObjRef goes out of scope, the reference count is automatically decreased. Because operations and attribute getters return an object with the reference count already increased, their C++ signature uses another template in the return type, already_AddRefd<T> in place of T*; assigning an already_AddRefd<T> into an ObjRef<T> means that the reference count is not increased, but it will be decreased when the ObjRef goes out of scope. To QueryInterface, the inline method QueryInterface wraps an object in a DoQueryInterface class, which can be assigned into an ObjRef<T> to cause a query_interface to the interface corresponding to type T, with all reference counting handled correctly. It is also valid to call QueryInterface directly on an already_AddRefd, in which case the reference count on the original object will be decreased. ObjRef objects can be used as if they were pointers, and can be converted into pointers.

Bootstrapping the CellML API

The language-independent part of the CellML API documentation describes what interfaces objects can support, and how to get different objects by performing operations and retrieving attributes on the existing objects. However, there is a chicken-and-egg problem - you need to get the first object before you can do anything on an interface. This problem is solved differently in different language bindings. In C++, the reference implementation solves the problem using global methods, called Bootstrap Methods, that return interface pointers.

Here are some of the bootstrap methods available in the CellML API:

ServiceBootstrap methodReturn typeHeader
CoreCreateCellMLBootstrapiface::cellml_api::CellMLBootstrapCellMLBootstrap.hpp
AnnoToolsCreateAnnotationToolServiceiface::cellml_services::AnnotationToolServiceAnnoToolsBootstrap.hpp
CCGSCreateCodeGeneratorBootstrapiface::cellml_services::CodeGeneratorBootstrapCCGSBootstrap.hpp
CGRSCreateGenericsServiceiface::cellml_services::GenericsServiceCGRSBootstrap.hpp
CISCreateIntegrationServiceiface::cellml_services::CellMLIntegrationServiceCISBootstrap.hpp
CUSESCreateCUSESBootstrapiface::cellml_services::CUSESBootstrapCUSESBootstrap.hpp
CeLEDSCreateCeLEDSBootstrapiface::cellml_services::CeLEDSBootstrapCeLEDSBootstrap.hpp
CeLEDSExporterCreateCeLEDSExporterBootstrapiface::cellml_services::CeLEDSExporterBootstrapCeLEDSExporterBootstrap.hpp
CeVASCreateCeVASBootstrapiface::cellml_services::CeVASBootstrapCeVASBootstrap.hpp
MaLAESCreateMaLaESBootstrapiface::cellml_services::MaLaESBootstrapMaLaESBootstrap.hpp
SProSCreateSProSBootstrapiface::SProS::BootstrapSProSBootstrap.hpp
SRuSCreateSRuSBootstrapiface::SRuS::BootstrapSRuSBootstrap.hpp
TeLICeMSCreateTeLICeMServiceiface::cellml_services::TeLICeMServiceTeLICeMSService.hpp
VACSSCreateVACSServiceiface::cellml_services::VACSServiceVACSSBootstrap.hpp
Back to Top