Advanced Extension Programming
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 CodaTutorial). 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 copy DEFCONST's definition from defun.h into the mentioned 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 protected constants re-installs their original values.
#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, DEFVAR is defined in defun.h and not in defun-dld.h, so the programmer must introduce the macro herself.
// 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 represents 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 );
#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;
}
Documenting Constants And Variables
Having studied CodaTutorial, only one new Texinfo function remains: defvr.
-*- texinfo -*-
@defvr {Built-in Variable} my_own_variable
...
@end defvr
Using static variables
In functions, you can declare variables as being 'persistent' or 'static'. In other words, their status is not lost during successive calls of the function.
As an example, examine:
function static_test() persistent m = 0; m++; disp(m); endfunction
Which behaves as follows:
octave:1> static_test 1 octave:2> static_test 2 octave:3> static_test 3
Can we do the same in an oct-file? Sure!
#include <octave/oct.h>
static int m = 0;
DEFUN_DLD (static_test, args, , "")
{
m++;
octave_stdout << m << std::endl;
return octave_value();
}
Accessing global variables
Examine the following snippet:
#include <octave/oct.h>
DEFUN_DLD (global_test, args, , "")
{
octave_value MyGlobal = get_global_value ("MyGlobal");
if (error_state)
{
error ("foo: global symbol MyGlobal not found");
return octave_value();
}
int val = MyGlobal.int_value ();
if (error_state)
{
error ("foo: global symbol MyGlobal is not an integer");
return octave_value();
}
octave_stdout << "MyGlobal: " << val << std::endl;
return octave_value ();
}
The function get_global_value is used to access the global. Now test it in the interpreter:
No global is defined and we try to access it:
octave:1> global_test error: get_global_by_name: unknown symbol `MyGlobal' error: foo: global symbol MyGlobal not found
We define the global and successfully access it:
octave:1> global MyGlobal = 1; octave:2> global_test MyGlobal: 1
Notice how test_global uses error_state to see whether MyGlobal is of the correct type. This is good practise!