1.2. Advanced Extension Programming

...

1.2.1. Defining Constants And Variables

The definition of constants and variables in a dynamically linked GNU/Octave extension resembles the header of a dynamically linked function (see, for example, Section 1.1.3). However, the appropriate macro DEFCONST is not available when creating a dynamically loadable extension for it is defined in defun.h and not in defun-dld.h. The latter is necessary to set up an dynamically loadable extension. The easiest, moderately clean way is to duplicate DEFCONST's definition from defun.h into the respective extension.

// same definition as found in defun.h

#ifndef DEFCONST
#define DEFCONST(name, defn, doc) DEFCONST_INTERNAL(name, defn, doc)
#endif

DEFCONST introduces a constant named constant_name at the interpreter level, giving it the value of defining_expression and endowing it with the documentation documentation_string. The newly created constant will be protected against deletion, but not against change.

void
DEFCONST(constant_name,
         defining_expression,
         const std::string& documentation_string)

The name of the constant constant_name must be a valid C++ identifier, because it is not quoted. Octave automatically casts the variable's definition, defining_expression, to type octave_value.

A constant can be assigned to and then takes on the new value! Assigning to a constant does not even produce a warning. clearing a protected constant does not give raise to a warning either. Clearing a protected constants re-installs its original value.

Example 1-7. Constant Definition

#include <oct.h>

// `DEFCONST' from "defun.h"
#ifndef DEFCONST
#define DEFCONST(name, defn, doc) \
    DEFCONST_INTERNAL(name, defn, doc)
#endif

static const double h = 6.626176e-34; // Planck's constant in J*s

DEFUN_DLD(defconst, args, ,
          "Install some fundamental physical constants.")
{
    if (args.length() == 0)
    {
        DEFCONST(c, 2.99792458e8,
                 "Speed of light in m/s.");
        DEFCONST(hbar, h / (2.0 * M_PI),
                 "Reduced Planck's constant hbar, that is, h/(2*pi) in J*s.");
        DEFCONST(G, 6.672e-11,
                 "Graviation constant in N*m^2/kg^2.");
        DEFCONST(e, 1.6021892e-19,
                 "(Absolute value of the) Charge of an electron in C.");
    }
    else
    {
        error("defconst: expecting no arguments.");
    }

    return octave_value_list();
}
    

Tip

Long documentation strings in a long series of definitions tend to obscure the code. Assigning the documentation string to a macro allows for a separation of the help text and the definition.

#define DOCSTRING_HBAR \
"Reduced Planck's constant hbar, this is, h/(2*pi) in J*s."

...

DEFCONST(hbar, h / (2.0 * M_PI), DOCSTRING_HBAR);
    

This is also useful for describing the function's documentation string.

Like DEFCONST is defined in defun.h and not in defun-dld.h. So, the programmer must introduce the macro himself.

// same definition as found in defun.h

#ifndef DEFVAR
#define DEFVAR(name, defn, chg_fcn, doc) \
    DEFVAR_INTERNAL(#name, SBV_ ## name, defn, false, chg_fcn, doc)
#endif
void
DEFVAR(variable_name,
       defining_expression,
       symbol_record::change_function changing_function,
       const std::string& documentation_string)

The parameters variable_name, defining_expression, and documentation_string are analogous to those of DEFCONST. Only changing_function calls for further explanation.

changing_function is a pointer to a function that gets called whenever variable variable_name is given a new value. changing_function can be NULL if there is no function to call. change_function is defined in symtab.h

// symtab.h

typedef int (*change_function) (void);

A changing_function never takes on any parameters! Therefore, it must have a built-in knowledge of which interpreter variable to take care of. Usually, changing_functions correspond one-to-one with variable_names. Note the changing_function is called to initialize variable_name with the value of defining_expression. This means that changing_function is called at least once even if variable_name never gets changed withing the interpreter. The return value 0 from changing_function signals success to the caller, any other value stands for failure.

DEFVAR installs and initializes a variable in the interpreter's workspace. To access a variable or constant, variables.h declares three functions:

#include <variables.h>
    

std::string builtin_string_variable ( const std::string& symbol_name );

int builtin_real_scalar_variable ( const std::string& symbol_name , double& value );

octave_value builtin_any_variable ( const std::string& symbol_name );

Example 1-8. Variable Definition

#include <oct.h>
#include <variables.h>            // for `builtin_*_variable'

// `DEFVAR' from "defun.h"
#ifndef DEFVAR
#define DEFVAR(name, defn, chg_fcn, doc) \
    DEFVAR_INTERNAL(#name, SBV_ ## name, defn, false, chg_fcn, doc)
#endif

static double counter_var;
static unsigned count = 0;

static int counter_set();

//
// documentation strings
//

#define DOCSTRING_DEFVAR \
"Define two variables in the workspace: simple and counter.\n\
See the respective documentations, that is,\n\
`help simple' and `help counter'."

#define DOCSTRING_SIMPLE \
"Variable `simple' is initialized to 0.5.\n\
It is not linked to any low-level variable."

#define DOCSTRING_COUNTER \
"Variable `counter' is initialized to 1.0.\n\
It is linked to the C++ variable `counter_var' in file `defvar.cc'.\n\
Whenever `counter' is assigned to the number of assigments\n\
is printed."

//
// body
//

DEFUN_DLD(defvar, args, , DOCSTRING_DEFVAR)
{
    if (args.length() == 0)
    {
        DEFVAR(simple, 0.5, 0, DOCSTRING_SIMPLE);
        DEFVAR(counter, 1.0, counter_set, DOCSTRING_COUNTER);
    }
    else
    {
        error("defvar: expecting no arguments.");
    }

    return octave_value_list();
}

static int
counter_set()
{
    if (builtin_real_scalar_variable("counter", counter_var) == 0)
    {
        error("counter_set: internal error, non-existent variable");
        return 1;
    }

    count++;
    cout << "==> `counter' has been assigned to " << count << " times;\n"
         << "==> its new value is " << counter_var << ".\n";

    return 0;
}
    

1.2.2. Documenting Constants And Variables

Having studied Section 1.1.4.2, only one new Texinfo function is waiting: defvr.

-*- texinfo -*-
@defvr {Built-in Variable} my_own_variable
...
@end defvr