This post is about creating plugins to the Windows version of GNU Octave 4.0.0 (Octave) using existing components created with Microsoft Visual Studio 2013 (MSVC). It takes too long to explain why this is sometimes useful, just assume that it is. A typical scenario could be that you have some kind of database accessible from MSVC code and you want to expose the data in Octave.
The figure below illustrates a possible setup. To implement a plugin in Octave, you write a piece of C++ code and compile/link it into a special kind of shared library referred to as an 'oct-file'. Such code can call other components, for example a DLL (Dynamic Link Library) created with MSVC. This way, the oct-file functions as the glue between the Octave application and some other software component.
This sounds simple enough, but in practice there are a couple of things to handle to make it work:
First, Octave and its oct-files are compiled using the MinGW GNU g++ C++ compiler (It is not practical to recompile Octave using MSVC) and GNU g++ code is not binary compatible with MSVC code. Therefore, we cannot statically link the MSVC dll with the oct-file and we cannot pass C++ objects in the calls between them, because name mangling schemes and calling conventions are incompatible between the compilers.
Second, Octave and its oct-files are compiled as 32bit. Even if we find a way around the first problem, it will not work if the MSVC component is compiled as 64bit, it has to be 32bit as Octave.
Now the whole thing sounds a lot more complicated, but the problem description also provides the clues to the solution
Use a C interface in the calls from the oct-file (GNU g++) to the MSVC code
Load the MSVC dll dynamically instead of linking statically
Compile the MSVC code as 32 bit
MSVC C++ code
The first bullet above means we must provide global functions declared as extern āCā in the MSVC C++ code (linked as DLL, and exported), here is a sample header declaration:
#ifndef MSVC_COMP_H
#define MSVC_COMP_H
#ifdef MSVC_COMP_IMPLEMENTATION
#else
#endif
extern "C" {
}
#endif // MSVC_COMP_H
Note that the function takes and returns parameters as if it was an old style C-function, no C++ objects or pointers are allowed, du to the compiler differences. The extern āCā statement removes any name mangling in the compiled name, it makes it possible to look up from other code (see below).
Notice also that in this case, the function returns a pointer to some numerical data. Such data may be dynamically allocated by the MSVC compiler, and cannot be deleted in the GNU code (in this case the pointer ponts to a global variable internally in the MSVC code and it will be cleaned up in the next call).
The above is just the header file declaration, the implementation of the msvc_get_data function can use all C++ constructs, objects, pointers etc.
OCT-file code
The second bullet in our list, and how the OCT-file interfaces the MSVC code is best illustrated using an example (note that most error checking has been omitted for clarity).
File oct_get_data.cpp:
#include <octave/oct.h>
#include <fstream>
#include <windows.h>
#include <dMatrix.h>
// function pointer to the MSVC fuunction
typedef double* (*msvc_gdfunc)(const char* file_path, const char* data_id, long* nsamp);
// Octave function declaration
DEFUN_DLD (oct_get_data, args, nargout, "oct_get_data String")
{
}
The code above declares a function pointer type for our MSVC function. It then uses the Windows API to load the DLL dynamically + look up the pointer to the function. If found, it calls the MSVC function and constructs a suitable matrix object to return to the Octave application.
Obviously much more rigorous error checking is in order.
Compiling the OCT-file
As previously mentioned, the oct-file must be compiled using the GNU C++ compiler to be compatible with Octave. For this purpose we use the mkoctfile utility. It can be done within Octave, or via a Wiindows batch script as shown below.
File cppoct.bat:
@echo off
REM Script to compile C++ into Octave oct-files
REM
REM configure Octave oct compiler
set OCT_VER=4.0.0
set OCT_HOME=C:\Octave\Octave-%OCT_VER%
set OCT_BIN=%OCT_HOME%\bin
set OCT_INC=%OCT_HOME%\include\octave-%OCT_VER%
set OCT_LIB=%OCT_HOME%\lib\octave\%OCT_VER%
REM
REM set Octave bin dir first in path so g++ can be found
set PATH=%OCT_BIN%;%PATH%
REM
REM turn on echo so we can see what is going on as we compile
@echo on
%OCT_BIN%\mkoctfile.exe -I%OCT_INC% -I%OCT_INC%\octave
@echo off
REM tidy up intermediate files
del *.o
Running this script for the oct_get_data.cpp file generates the oct_get_data.oct file.
Using the plugin in Octave
Once the oct-file and the msvc dll exist, it is recommended to store them in a common folder in the file system. In Octave, you then need to specify that folder using 'addpath'.
addpath("C:\\somefolder");
data = oct_get_data("myfile.dat","whatever");
plot( data(:,1), data(:,2));
Assuming the data returned was a matrix of X,Y data, the result could look something like below
If you find this useful, please add a comment below :-)
Appreciate this post. Let me try it out.
Hello
thank you very much for this information!
I’ve just tested this recipe with a simple example and I can confirm that this recipe also works under the following conditions (as of 24. Nov. 2016):
– Windows 10
– 64 bits version of Octave (Version 4.2.0, installed from downloadable installer).
– External DLL compiled with Intel Parallel Studio 16.0 or MSVC 2013
In my humble opinion, this recipe should be part of the Octave tutorial that explains how to work with external DLLs and OCT files. It took me a while to find it with google.
Kind regards,
Pablo Stickar
Hi Pablo, thank you for the confirmation and info on 64bit version of Octave with MSVC 2013. That is good info. If anybody wants to add this info to Octave documentation, please feel free to do so.
Hi I was trying to use Octave to control instrument through TEKVISA. While I can create vnactl3.oct (using mkoctfile -L./ -ltkVisa64 ./vnactl3.cpp) successfully, Octave will always crash when I call vnactl3. Is there fundamental problem making oct file this way?
Please note that “TEKVISA” related code (started with vi…) has been independently verified in MS Visual Studio 2017 (for 64bit).
//vnactl3.cpp
#include
#include
#include “TekVisa.h”
#include
DEFUN_DLD (vnactl3, args, nargout,
“VNA Control 3 Help String”)
{
ViSession rm = VI_NULL, vi = VI_NULL;
ViStatus status;
status = viOpenDefaultRM(&rm);
viClose(rm);
return octave_value_list ();
}
If Octave is 32 bit, make sure your MSVC code is 32bit as well
Tks. Octave is 64 bit, TEKVSIA LIB is for 64 bit too.
Notice the extern “C” part around the msvc function prototype. Not sure if you have that, but it is required to make the calls compatible across compilers. Could also be something else, I have not done this in a while.