Instantiate an instance of C++ object via MATLAB Coder

I have an interesting task. I would like to call a GNU Octave interpreter via MATLAB Coder. I have custom C++ source code that initialises and prepares Octave interpreter. My current problem lies in making MATLAB coder aware of the C++ `interpreter` instance. Please find below by current attempt:
function [status] = inter_c() %#coder
%INTER_C Initialises Octave interpreter
% Initialises Octave interpreter
coder.cinclude('/opt/local/include/octave-6.1.0/octave/oct.h');
coder.cinclude('/opt/local/include/octave-6.1.0/octave/interpreter.h');
coder.updateBuildInfo('addSourceFiles', 'inter.cpp');
inter = coder.opaque('octave::interpreter'); % <-- error occurs here
status = 0;
status = coder.ceval('initOctInter', coder.ref(inter));
end
When I try to generate the resulting C++ code with
codegen inter_c -lang:c++ -args {} -report
It crashes with the following error:
??? Variable 'inter' is not fully defined on some execution paths.
I understand, that I need to let MATLAB coder know all the details of the `interpreter` definition, but I don't know, how to do that correctly. I also tried to direct coder to the relevant header file with
inter = coder.opaque('interpreter', 'HeaderFile', '/opt/local/include/octave-6.1.0/octave/interpreter.h');
But the error remains the same.
The contents of custom C++ code in `inter.cpp` are:
#include <iostream>
#include <oct.h>
#include <octave.h>
#include <parse.h>
#include <interpreter.h>
// Initialises Octave interpreter
int initOctInter(interpreter &inter) {
int status = -1;
// Inhibit reading history file
inter.initialize_history(false);
// Set custom load path
inter.initialize_load_path(false);
// Initialise interpreter
inter.initialize();
status = (inter.initialized()) ? 0 : -1;
if (status != 0) {
cerr << "Octave interpreter initialisation failed!" << endl;
}
// Make interpreter ready to execute
status = inter.execute();
if (status != 0) {
cerr << "Creating embedded Octave interpreter failed!" << endl;
}
return status;
}
In C++ this task is achieved as follows:
#include <interpreter.h>
int main(void) {
interpreter inter;
// Initialise Octave interpreter
int status = initOctInter(inter);
if (status == 0) {
// Call compute functions
// ...
// Shutdown Octave interpreter (releases memory)
inter.shutdown();
}
return 0;
}
I would appreciate any help and guidance. If possible, would you please direct me to a relevant example? Thank you!

12 Comments

I am not very sure what is happening here.
Can you please try to initialize your variable used for "coder.opaque" like shown in the "FILE *" example :
You may have to change the code like below :
coder.opaque('octave::interpreter*','nullptr','HeaderFile', '/opt/local/include/octave-6.1.0/octave/interpreter.h')
Then change the interface like below :
int initOctInter(interpreter* inter) {...}
This is just my guess, cannot guarantee you that this will work.
Another thing to try out.
Try to have an initialization for the opaque variable.
inter = coder.opaque('octave::interpreter');
inter = coder.ceval('init_interpreter'); % Initializes the variable
Thank you very much for your help and suggestions. The first option results in the following error message from MATLAB coder:
inter_c.cpp:27:10: error: use of undeclared identifier 'initOctInter'
return initOctInter(&inter);
I don't fully understand your second option. I intended to use the C++ function `initOctInter` to initialise the Octave interpreter. That is its purpose. But it needs to take the interpreter `inter` variable as an input parameter. This is what I'm trying to achieve by calling:
status = coder.ceval('initOctInter', coder.ref(inter));
I think my problem is to let MATLAB know, what is `inter` and how to work with it.
I guess MATLAB Coder is not able to determine that the variable "inter" is being assigned. The assignment is being done through coder.ref() indirectly, but I am suspecting that the MATLAB Coder is not able to see that.
By doing coder.opaque(), you are just declaring the type, you are not acutally instantiating the object according to my understanding. So I guess you will have to care of the actual allocation of the object.
For example in your previous question "create_point" was actually creating the struct and returing it. I guess you will have to do the similar thing here.
I'm looking at Octave source code. This is how interpreter is defined:
// Create an interpreter object and perform initialization up to the
// point of setting reading command history and setting the load
// path.
interpreter::interpreter (application *app_context)
: m_app_context (app_context),
m_tmp_files (),
m_atexit_fcns (),
m_display_info (),
m_environment (),
m_settings (),
m_error_system (*this),
m_help_system (*this),
m_input_system (*this),
m_output_system (*this),
m_history_system (*this),
m_dynamic_loader (*this),
m_load_path (*this),
m_load_save_system (*this),
m_type_info (),
m_symbol_table (*this),
m_evaluator (*this),
m_stream_list (*this),
m_child_list (),
m_url_handle_manager (),
m_cdef_manager (*this),
m_gtk_manager (),
m_event_manager (*this),
m_gh_manager (nullptr),
m_interactive (false),
m_read_site_files (true),
m_read_init_files (m_app_context != nullptr),
m_verbose (false),
m_traditional (false),
m_inhibit_startup_message (false),
m_load_path_initialized (false),
m_history_initialized (false),
m_interrupt_all_in_process_group (true),
m_cancel_quit (false),
m_executing_finish_script (false),
m_executing_atexit (false),
m_initialized (false)
{
This looks too involved to be instantiated from MATLAB. I don't understand, why do we have to instantiate it from MATLAB. Maybe it is possible to call the native C++ function to do it? What we need to do is to make MATLAB aware of what `interpreter` is, what it is consists of and how to deal with it. Minimize MATLAB code and rely as much as possible onto native C++ code. My problem still remains, I don't know how to make MATLAB aware of the `interpreter` object. In other words how do I declare an object of type interpreter to work with it in MATLAB?
Here is an exerpt from the `interpreter.h` file with declaring the relevant functions:
class OCTINTERP_API interpreter
{
public:
// Create an interpreter object and perform basic initialization.
interpreter (application *app_context = nullptr);
// No copying, at least not yet...
interpreter (const interpreter&) = delete;
interpreter& operator = (const interpreter&) = delete;
// Clean up the interpreter object.
~interpreter (void);
void intern_nargin (octave_idx_type nargs);
// If creating an embedded interpreter, you may inhibit reading
// the command history file by calling initialize_history with
// read_history_file = false prior to calling initialize.
void initialize_history (bool read_history_file = false);
// If creating an embedded interpreter, you may inhibit setting
// the default compiled-in path by calling initialize_load_path
// with set_initial_path = false prior calling initialize. After
// that, you can add directories to the load path to set up a
// custom path.
void initialize_load_path (bool set_initial_path = true);
// Load command line history, set the load path.
void initialize (void);
// ...
}
It seems to me, that this cublas example may be of use. Variables of custom datatypes may be defined with `coder.opaque`:
handle = coder.opaque('cublasHandle_t');
Then this variable is initialised with:
ret = coder.ceval('cublasCreate', coder.wref(handle));
Did you try using coder.wref() instead of coder.ref() ?
Here is another iteration of my MATLAB function intended for Coder:
function [status] = inter_c() %#coder
%INTER_C Initialises Octave interpreter
% Initialises Octave interpreter
coder.updateBuildInfo('addCompileFlags', '-I/opt/local/include/octave-6.1.0/octave');
coder.updateBuildInfo('addIncludeFiles', 'oct.h');
coder.updateBuildInfo('addIncludeFiles', 'octave.h');
coder.updateBuildInfo('addIncludeFiles', 'parse.h');
coder.updateBuildInfo('addIncludeFiles', 'interpreter.h');
coder.updateBuildInfo('addLinkObjects', 'liboctave.lib', '/opt/local/lib/octave/6.1.0/', '', true, true);
coder.updateBuildInfo('addLinkObjects', 'liboctinterp.lib', '/opt/local/lib/octave/6.1.0/', '', true, true);
coder.updateBuildInfo('addSourceFiles', 'inter.cpp');
coder.updateBuildInfo('addIncludeFiles', 'inter.h');
coder.cinclude('oct.h');
coder.cinclude('octave.h');
coder.cinclude('parse.h');
coder.cinclude('interpreter.h');
coder.cinclude('inter.h');
inter = coder.opaque('octave::interpreter', 'HeaderFile', '/opt/local/include/octave-6.1.0/octave/interpreter.h');
status = 0;
status = coder.ceval('initOctInter', coder.ref(inter));
end
I would like to pass the Octave intepreter instance called `inter` by reference. Since it is C++ the `initOctInter` signature is:
#include <octave.h>
#include <interpreter.h>
using namespace octave;
int initOctInter(interpreter &inter);
// @eof inter.h
Please see the StackOverflow answer of Sean Ramey for an example.
My new attempt still crashes with the following error:
codegen inter_c -lang:c++ -args {} -report
??? Variable 'inter' is not fully defined on some execution paths.
For completeness please find below the contents of `inter.cpp` file and `test_inter.cpp` (native C++ code for calling Octave interpreter):
#include <iostream>
#include <oct.h>
#include <octave.h>
#include <parse.h>
#include <interpreter.h>
using namespace std;
using namespace octave;
// Initialises Octave interpreter
int initOctInter(interpreter &inter) {
int status = -1;
try {
status = inter.execute();
}
catch (const exit_exception& ex) {
}
catch (const execution_exception&) {
}
return status;
}
// @eof inter.cpp
C++ source code of `test_inter.cpp`:
#include <iostream>
#include <oct.h>
#include <octave.h>
#include <parse.h>
#include <interpreter.h>
#include "inter.h"
using namespace std;
using namespace octave;
int main(int argc, char *argv[]) {
interpreter inter;
int status = initOctInter(inter);
cout << "status:" << status << endl;
// Shutdown interpreter
inter.shutdown();
return 0;
}
// @eof test_inter.cpp
Changing `coder.ref(inter)` to `coder.wref(inter)` makes Coder crash with another error:
inter_c.cpp:31:10: error: no matching function for call to 'initOctInter'
return initOctInter(&inter);
^~~~~~~~~~~~
/Users/mabalenk/repo/git/alc/rascal/oct/inter.h:6:5: note: candidate function not viable: no known conversion from 'octave::interpreter *' to 'octave::interpreter &' for 1st argument; remove &
int initOctInter(interpreter &inter);
But I don't believe this is a correct approach.

Sign in to comment.

Answers (2)

From my understanding you want to Instantiate an instance of C++ object. Refer similar answer which might help you.

1 Comment

I was hoping to avoid writing custom wrappers to C++ functions. I wanted to call basic interface functions similar to `initOctInt()` and pass them the instance of an object and let native C++ deal with the object methods. But it seems I will still have to go that route. I feel it is too complicated for an average MATLAB user. Time to study your `person` example.

Sign in to comment.

I tried to solve this issue by creating the similar example.
I have mocked up some of the files using my own definition of the interpreter. Please go through the attached code.
I guess you were almost there using coder.wref(), only thing was you need to change your interface to take pointer (*) not as referece (&).
Hope this will be helpful for you.
codegen inter_c -lang:c++ -args {} -report

8 Comments

Perfect! Thank you, @Darshan Ramakant Bhat! I think we are on the right track!
Now I encounter a problem with C++ header files and include paths:
/opt/local/include/octave-6.1.0/octave/ov.h:54:7: error: definition of type 'mxArray' conflicts with typedef of the same name
class mxArray;
^
/Applications/MATLAB_R2020b.app/extern/include/matrix.h:157:28: note: 'mxArray' declared here
typedef struct mxArray_tag mxArray;
It seems `mxArray` is defined both in Octave and MATLAB and these definitions are different. Is there a way to avoid such conflicts, e.g. by omitting:
coder.updateBuildInfo('addCompileFlags', '-I/opt/local/include/octave-6.1.0/octave');
and replacing it with something like:
coder.updateBuildInfo('addIncludeFiles', '/opt/local/include/octave-6.1.0/octave/oct.h');
In other words include only those Octave header files that are necessary. I think now all header files (*.h) are included due to
coder.updateBuildInfo('addCompileFlags', '-I/opt/local/include/octave-6.1.0/octave');
I'm trying to fix it now...
I guess that definition is getting included due to the below code
coder.cinclude('oct.h');
coder.cinclude('octave.h');
coder.cinclude('parse.h');
coder.cinclude('interpreter.h');
coder.cinclude('inter.h');
One of the header included here may include the above header by transitive closure.
You can inspect the generated code, those should include only above mentioned headers.
These are all of the header files included into the `inter_c.cpp` file generated by MATLAB Coder:
#include "inter_c.h"
#include "rt_nonfinite.h"
#include "/opt/local/include/octave-6.1.0/octave/interpreter.h"
#include "inter.h"
#include "interpreter.h"
#include "oct.h"
#include "octave.h"
#include "parse.h"
It looks that `interpreter.h` is included twice. Maybe this is the problem? I will check. Thank you very much for your help!
Here is yet another (minimalistic) iteration of my MATLAB function:
function [status] = inter_c() %#coder
%INTER_C Initialises Octave interpreter
% Initialises Octave interpreter
coder.updateBuildInfo('addCompileFlags', '-I/opt/local/include/octave-6.1.0/octave');
coder.updateBuildInfo('addLinkObjects', 'liboctave.lib', '/opt/local/lib/octave/6.1.0/', '', true, true);
coder.updateBuildInfo('addLinkObjects', 'liboctinterp.lib', '/opt/local/lib/octave/6.1.0/', '', true, true);
coder.cinclude('inter.h');
inter = coder.opaque('octave::interpreter');
status = 0;
status = coder.ceval('initOctInter', coder.wref(inter));
end
I tried to include only what was necessary, but I still receive the same error:
In file included from inter_c.cpp:15:
In file included from /Users/mabalenk/repo/git/alc/rascal/oct/inter.h:2:
In file included from /opt/local/include/octave-6.1.0/octave/interpreter.h:40:
In file included from /opt/local/include/octave-6.1.0/octave/cdef-manager.h:31:
In file included from /opt/local/include/octave-6.1.0/octave/cdef-class.h:37:
In file included from /opt/local/include/octave-6.1.0/octave/cdef-method.h:37:
In file included from /opt/local/include/octave-6.1.0/octave/cdef-object.h:36:
In file included from /opt/local/include/octave-6.1.0/octave/error.h:37:
In file included from /opt/local/include/octave-6.1.0/octave/oct-map.h:36:
In file included from /opt/local/include/octave-6.1.0/octave/Cell.h:36:
/opt/local/include/octave-6.1.0/octave/ov.h:54:7: error: definition of type 'mxArray' conflicts with typedef of the same name
class mxArray;
^
/Applications/MATLAB_R2020b.app/extern/include/matrix.h:157:28: note: 'mxArray' declared here
typedef struct mxArray_tag mxArray;
^
I have also changed my C++ source and header files to use headers sparringly. But this still didn't help.
inter.cpp:
#include <iostream>
#include "inter.h"
// ...
inter.h:
#include <interpreter.h>
// ...
The problem looks to be that both the definitions are reachable. I cannot think of a good solution unless you rename the definition in ov.h
Thank you @Darshan Ramakant Bhat for your suggestion. But this is a very intrusive solution, that I would like to avoid for now. I made an inquiry about it on the GNU Octave mailing list. Let's see, what they respond. Otherwise, we will have to take this route ;)

Sign in to comment.

Products

Release

R2020b

Asked:

on 1 Mar 2021

Edited:

on 8 Mar 2021

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!