Generating multiple *.oct* files from a single *.cc* source file to interface a C library to Octave

Viewed 309

I have a C library that I want to use from within Octave. Following the tutorial, it seems straight forward: wrap the functions in C++ then mkoctfile them. The problem is: what if I want to have multiple functions definitions (wrappers) in a single source file?

In the mentioned tutorial it is stated

It should be noted that it is perfectly acceptable to have more than one DEFUN_DLD function in a source file. However, there must either be a symbolic link to the oct-file for each of the functions defined in the source code with the DEFUN_DLD macro or the autoload (Function Files) function should be used.

Then in the provided link:

Once Octave finds a file with a name that matches (the called function), the contents of the file are read. If it defines a single function, it is compiled and executed. See Script Files, for more information about how you can define more than one function in a single file.

In this second link, there is no info as to how to load a .oct file with multiple functions in it or how to generate multiple .oct files from a single source file. From what I've understood, the later is the correct approach. How can I do that?

4 Answers

The point of the second link is that you don't load a .oct file with multiple functions in it - at least not from octave's perspective. That's what the symlinks are for - you have symbols A, B, and C in there? Make A.oct, B.oct, and C.oct symbolic links that point at that file and you can use them as if each contained only the symbol you care about.

If you have multiple function definitions in a single oct file, you use autoload(). So if you have foo.oct which has functions foo and bar, then you do:

autoload ("bar", "path-to-foo.oct");

I'll start by clarifying the second quote-window in your question. This is not referring specifically to .oct defined functions. What this is implying is the difference between a canonical m-file defined function, and 'on-the-spot' functions defined directly in the console or as part of a script.

As for the first quote-window, when it comes to functions that are defined in .oct files, the situation is different. What it's saying is that you can create an .oct file that defines many functions, but in order to call these functions, there needs to be a file by the same name in your path. So if an .oct file defines functions "foo" and "bar", you need to have one copy of the .oct file called "foo.oct", and another (or, more realistically, as symbolic link to the original) renamed as "bar.oct".

Similarly, you can also define a "foo.m" and "bar.m" file in your workspace, which only contains the documentation for those functions, such that if you then do "help foo" or "help bar" you get the intended documentation out.

Alternatively, you can use autoload, as carandraug suggested.

Another possibility to generate a C to Octave interface is using SWIG which can generate a single .oct file with all your functions. Refer to here when using pointers and arrays.

Here is an example:

header

/* File: example.h */

int fact(int n);

int fact2(int n1, int n2);

void add(int *x, int *y, int *r);

source

/* File: example.c */

#include "example.h"

int fact(int n) {
    if (n < 0){ /* This should probably return an error, but this is simpler */
        return 0;
    }
    if (n == 0) {
        return 1;
    }
    else {
        /* testing for overflow would be a good idea here */
        return n * fact(n-1);
    }
}

int fact2(int n1, int n2) {
    return fact(n1)*fact(n2);
}

void add(int *x, int *y, int *r) {
    *r = *x + *y;
}

interface

/* File example.i */
%module swigexample
%include "cpointer.i"

%{
#include "example.h"
%}

%pointer_functions(int, intp)

%include "example.h"

compile

swig -octave -o swigexample.cpp example.i
mkoctfile -I. -I/usr/include/octave-4.2.2/ -Iswiglib swigexample.cpp example.c

test

% File test.m
swigexample;
fact(5)
fact2(4,4)
% ==============
a = new_intp();
intp_assign(a, 37);
b = new_intp();
intp_assign(b, 22);
c = new_intp();
add(a,b,c);
r = intp_value(c);
delete_intp(a);
delete_intp(b);
delete_intp(c);
r
Related