2.4 Overview (version 2)
AllApplicationManualNameSummaryHelp

  • Documentation
    • Reference manual
    • Packages
      • A C++ interface to SWI-Prolog
        • A C++ interface to SWI-Prolog (Version 2)
          • Overview (version 2)
            • Design philosophy of the classes
            • Summary of files
            • Summary of classes
            • Wrapper functions
            • Naming conventions, utility functions and methods (version 2)
            • Limitations of the interface
            • Linking embedded applications using swipl-ld

2.4.1 Design philosophy of the classes

See also section 2.4.5.

The general philosophy for C++ classes is that a "half-created" object should not be possible - that is, the constructor should either succeed with a completely usable object or it should throw an exception. This API tries to follow that philosophy, but there are some important exceptions and caveats. (For more on how the C++ and Prolog exceptions interrelate, see section 2.17.)

The various classes (PlAtom, PlTerm, etc.) are thin wrappers around the C interface's types (atom_t, term_t, etc.). As such, they inherit the concept of "null" from these types (which is abstracted as PlAtom::null, PlTerm::null, etc., which typically is equivalent to 0). Normally, you shouldn't need to check whether the object is "fully created", but if you do, you can use the methods is_null() or not_null().

Most of the classes have constructors that create a "complete" object. For example,

PlAtom foo("foo");

will ensure that the object foo is useable and will throw an exception if the atom can't be created. However, if you choose to create an PlAtom object from a atom_t value, no checking is done (similarly, no checking is done if you create a PlTerm object using the PlTerm_term_t constructor).

To help avoid programming errors, most of the classes do not have a default "empty" constructor. For example, if you with to create a PlAtom that is uninitialized, you must explicitly use PlAtom(PlAtom::null). This make some code a bit more cumbersome because you can't omit the default constructors in struct initalizers.

Many of the classes wrap long-lived items, such as atoms, functors, predicates, or modules. For these, it's often a good idea to define them as static variables that get created at load time, so that a lookup for each use isn't needed (atoms are unique, so PlAtom("foo") requires a lookup for an atom foo and creates one if it isn't found).

C code sometimes creates objects "lazily" on first use:

void my_function(...)
{ static atom_t ATOM_foo = 0;
   ...
  if ( ! foo  )
     foo = PL_new_atom("foo");
   ...
}

For C++, this can be done in a simpler way, because C++ will call a local “static” constructor on first use.

void my_function(...)
{ static PlAtom ATOM_foo("foo");
}

The class PlTerm (which wraps term_t) is the most used. Although a PlTerm object can be created from a term_t value, it is intended to be used with a constructor that gives it an initial value. The default constructor calls PL_new_term_ref() and throws an exception if this fails. The various constructors are described in section 2.9.1. Note that the default constructor is not public; to create a "variable" term, you should use the subclass constructor PlTerm_var().