mirror of
https://github.com/davisking/dlib.git
synced 2024-11-01 10:14:53 +08:00
Added struct support to matlab to C++ API
This commit is contained in:
parent
27274c17d9
commit
060cdd092f
@ -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)
|
||||
|
||||
|
@ -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&);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
|
55
dlib/matlab/example_mex_struct.cpp
Normal file
55
dlib/matlab/example_mex_struct.cpp
Normal 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"
|
||||
|
@ -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
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user