Added struct support to matlab to C++ API

This commit is contained in:
Davis King 2016-01-28 10:38:58 -05:00
parent 27274c17d9
commit 060cdd092f
4 changed files with 311 additions and 1 deletions

View File

@ -12,4 +12,5 @@ include(../cmake)
# add_mex_function(some_other_mex_function pthread dlib fftw)
add_mex_function(example_mex_function dlib)
add_mex_function(example_mex_callback dlib)
add_mex_function(example_mex_struct dlib)

View File

@ -6,6 +6,60 @@
#include <string>
// ----------------------------------------------------------------------------------------
class matlab_struct
{
/*!
WHAT THIS OBJECT REPRESENTS
This object lets you interface with MATLAB structs from C++. For example,
given a MATLAB struct named mystruct, you could access it's fields like this:
MATLAB way: mystruct.field
C++ way: mystruct["field"]
MATLAB way: mystruct.field.subfield
C++ way: mystruct["field"]["subfield"]
To get the values as C++ types you do something like this:
int val = mystruct["field"];
See also example_mex_struct.cpp for an example that uses this part of the API.
!*/
class sub;
public:
matlab_struct() : struct_handle(0),should_free(false) {}
~matlab_struct();
const sub operator[] (const std::string& name) const;
sub operator[] (const std::string& name);
bool has_field(const std::string& name) const;
const void* release_struct_to_matlab() { const void* temp=struct_handle; struct_handle = 0; return temp; }
void set_struct_handle(const void* sh) { struct_handle = sh; }
private:
class sub
{
public:
sub() : struct_handle(0), field_idx(-1) {}
template <typename T> operator T() const;
template <typename T> sub& operator= (const T& new_val);
const sub operator[] (const std::string& name) const;
sub operator[] (const std::string& name);
bool has_field(const std::string& name) const;
private:
friend class matlab_struct;
const void* struct_handle;
int field_idx;
sub& operator=(const sub&);
};
const void* struct_handle;
bool should_free;
matlab_struct& operator=(const matlab_struct&);
};
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------

View File

@ -0,0 +1,55 @@
// The contents of this file are in the public domain. See LICENSE_FOR_EXAMPLE_PROGRAMS.txt
#include "call_matlab.h"
#include "dlib/matrix.h"
using namespace dlib;
using namespace std;
/*
This mex function takes a MATLAB struct, prints a few of its fields,
and then returns a new struct.
For example, you can call this function in MATLAB like so:
input = {}
input.val = 2
input.stuff = 'some string'
output = example_mex_struct(input)
output.number
output.number2
output.sub.stuff
output.sub.some_matrix
*/
void mex_function (
const matlab_struct& input,
matlab_struct& output
)
{
int val = input["val"];
string stuff = input["stuff"];
if (input.has_field("val2"))
{
string val2 = input["val2"];
cout << "The optional val2 field was set to: " << val2 << endl;
}
cout << "val: "<< val << endl;
cout << "stuff: " << stuff << endl;
output["number"] = 999;
output["number2"] = 1000;
output["sub"]["stuff"] = "some other string";
matrix<double> m = randm(2,2);
output["sub"]["some_matrix"] = m;
}
// #including this brings in all the mex boiler plate needed by MATLAB.
#include "mex_wrapper.cpp"

View File

@ -711,6 +711,23 @@ namespace mex_binding
}
}
void validate_and_populate_arg(
long arg_idx,
const mxArray *prhs,
matlab_struct& arg
)
{
if (!mxIsStruct(prhs))
{
std::ostringstream sout;
sout << " argument " << arg_idx+1 << " must be a struct";
throw invalid_args_exception(sout.str());
}
arg.set_struct_handle(prhs);
}
void validate_and_populate_arg(
long arg_idx,
const mxArray *prhs,
@ -737,7 +754,6 @@ namespace mex_binding
arg.resize(size);
}
// ----------------------------------------------------------------------------------------
template <typename EXP>
@ -904,6 +920,14 @@ namespace mex_binding
}
}
void assign_to_matlab(
mxArray*& plhs,
matlab_struct& item
)
{
plhs = (mxArray*)item.release_struct_to_matlab();
}
void assign_to_matlab(
mxArray*& plhs,
const std::string& item
@ -2164,6 +2188,182 @@ void call_matlab (
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
template <typename T>
matlab_struct::sub::operator T() const
{
if (struct_handle == 0)
throw dlib::error("Attempt to access data in an empty struct.");
mxArray* temp = mxGetFieldByNumber((const mxArray*)struct_handle, 0, field_idx);
if (temp == 0)
throw dlib::error("Attempt to access data in an empty struct.");
T item;
try
{
mex_binding::validate_and_populate_arg(0,temp,item);
}
catch(mex_binding::invalid_args_exception& e)
{
std::ostringstream sout;
sout << "Struct field '" << mxGetFieldNameByNumber((const mxArray*)struct_handle, field_idx) << "' can't be interpreted as the requested type."
<< endl << e.msg;
throw dlib::error(sout.str());
}
return item;
}
const matlab_struct::sub matlab_struct::
operator[] (const std::string& name) const
{
if (struct_handle == 0)
throw dlib::error("Struct does not have a field named '" + name + "'.");
matlab_struct::sub temp;
temp.struct_handle = struct_handle;
temp.field_idx = mxGetFieldNumber((const mxArray*)struct_handle, name.c_str());
if (temp.field_idx == -1 )
throw dlib::error("Struct does not have a field named '" + name + "'.");
return temp;
}
matlab_struct::sub matlab_struct::
operator[] (const std::string& name)
{
if (struct_handle == 0)
{
// We make a struct from scratch and mark that we will free it unless it gets
// written back to matlab by assign_to_matlab().
mwSize dims[1] = {1};
const char* name_str = name.c_str();
struct_handle = mxCreateStructArray(1, dims, 1, &name_str);
should_free = true;
if (struct_handle == 0)
throw dlib::error("Error creating struct from within mex function.");
}
matlab_struct::sub temp;
temp.struct_handle = struct_handle;
if ((temp.field_idx=mxGetFieldNumber((mxArray*)struct_handle, name.c_str())) == -1)
{
if ((temp.field_idx=mxAddField((mxArray*)struct_handle, name.c_str())) == -1)
{
throw dlib::error("Unable to add field '"+name + "' to struct.");
}
}
return temp;
}
const matlab_struct::sub matlab_struct::sub::
operator[] (const std::string& name) const
{
if (struct_handle == 0)
throw dlib::error("Struct does not have a field named '" + name + "'.");
matlab_struct::sub temp;
temp.struct_handle = mxGetFieldByNumber((const mxArray*)struct_handle, 0, field_idx);
if (temp.struct_handle == 0)
throw dlib::error("Failure to get struct field while calling mxGetFieldByNumber()");
if (!mxIsStruct((const mxArray*)temp.struct_handle))
throw dlib::error("Struct sub-field element '"+name+"' is not another struct.");
temp.field_idx = mxGetFieldNumber((const mxArray*)temp.struct_handle, name.c_str());
if (temp.field_idx == -1 )
throw dlib::error("Struct does not have a field named '" + name + "'.");
return temp;
}
matlab_struct::sub matlab_struct::sub::
operator[] (const std::string& name)
{
if (struct_handle == 0)
throw dlib::error("Struct does not have a field named '" + name + "'.");
matlab_struct::sub temp;
temp.struct_handle = mxGetFieldByNumber((const mxArray*)struct_handle, 0, field_idx);
// We are replacing this field with a struct if it exists and isn't already a struct
if (temp.struct_handle != 0 && !mxIsStruct((const mxArray*)temp.struct_handle))
{
mxDestroyArray((mxArray*)temp.struct_handle);
temp.struct_handle = 0;
}
if (temp.struct_handle == 0)
{
mwSize dims[1] = {1};
temp.struct_handle = mxCreateStructArray(1, dims, 0, 0);
if (temp.struct_handle == 0)
throw dlib::error("Failure to create new sub-struct field");
mxSetFieldByNumber((mxArray*)struct_handle, 0, field_idx, (mxArray*)temp.struct_handle);
}
if ((temp.field_idx=mxGetFieldNumber((mxArray*)temp.struct_handle, name.c_str())) == -1)
{
if ((temp.field_idx=mxAddField((mxArray*)temp.struct_handle, name.c_str())) == -1)
{
throw dlib::error("Unable to add field '"+name + "' to struct.");
}
}
return temp;
}
bool matlab_struct::has_field (
const std::string& name
) const
{
if (struct_handle == 0)
return false;
return mxGetFieldNumber((const mxArray*)struct_handle, name.c_str()) != -1;
}
bool matlab_struct::sub::has_field (
const std::string& name
) const
{
if (struct_handle == 0)
return false;
mxArray* temp = mxGetFieldByNumber((const mxArray*)struct_handle, 0, field_idx);
if (temp == 0 || !mxIsStruct(temp))
return false;
return mxGetFieldNumber(temp, name.c_str()) != -1;
}
template <typename T>
matlab_struct::sub& matlab_struct::sub::operator= (
const T& new_val
)
{
// Delete anything in the field before we overwrite it
mxArray* item = mxGetFieldByNumber((mxArray*)struct_handle, 0, field_idx);
if (item != 0)
{
mxDestroyArray((mxArray*)item);
item = 0;
}
// Now set the field
mex_binding::assign_to_matlab(item, new_val);
mxSetFieldByNumber((mxArray*)struct_handle, 0, field_idx, item);
return *this;
}
matlab_struct::
~matlab_struct (
)
{
if (struct_handle && should_free)
{
mxDestroyArray((mxArray*)struct_handle);
struct_handle = 0;
}
}
// ----------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------
void call_matlab (
const function_handle& funct
)