Commit a780c3a7 authored by hbristow's avatar hbristow

Swapped out function assertions in favour of ArgumentParser

parent 8f62a52b
......@@ -59,22 +59,23 @@ The first thing you need to learn how to do is write a mex-file with Matlab cons
// this automatically includes opencv core.hpp and mex.h)
#include <opencv2/matlab/bridge.hpp>
using namespace cv;
using namespace std;
using namespace matlab;
using namespace bridge;
// define the mex gateway
void mexFunction(int nlhs, mxArray* plhs[],
int nrhs, const mxArray* prhs[]) {
// claim the inputs into scoped management
MxArrayVector raw_inputs(prhs, prhs+nrhs);
MxArrayVector raw(prhs, prhs+nrhs);
// add an argument parser to automatically handle basic options
ArgumentParser parser("my function");
parser.addVariant(1, 1, "opt");
MxArrayVector parsed_inputs = parser.parse(inputs);
MxArrayVector reordered = parser.parse(raw);
// if we get here, we know the inputs are valid. Unpack...
BridgeVector inputs(parsed_inputs);
// if we get here, we know the inputs are valid and reordered. Unpack...
BridgeVector inputs(reordered.begin(), reordered.end());
Mat required = inputs[0].toMat();
string optional = inputs[1].empty() ? "Default string" : inputs[1].toString();
......@@ -339,6 +340,37 @@ The MxArray object uses scoped memory management. If you wish to pass an MxArray
plhs[0] = mat.releaseOwnership();
```
mxarray.hpp also includes a number of helper utilities that make working in mex-world a little easier. One such utility is the `ArgumentParser`. `ArgumentParser` automatically handles required and optional arguments to a method, and even enables named arguments as used in many core Matlab functions. For example, if you had a function with the following signature:
```cpp
void f(Mat first, Mat second, Mat mask=Mat(), int dtype=-1);
```
then you can create an `ArgumentParser` as follows:
```cpp
ArgumentParser parser("f");
parser.addVariant(2, 2, "mask", "dtype");
MxArrayVector inputs = parser.parse(prhs, prhs+nrhs);
```
and that will make available the following calling syntaxes:
```matlab
f(first, second);
f(first, second, mask);
f(first, second, mask, dtype);
f(first, second, 'dtype', dtype, 'mask', mask); % optional ordering does not matter
f(first, second, 'dtype', dtype); % only second optional argument provided
f(first, second, mask, 'dtype', dtype); % mixture of ordered and named
```
Further, the output of the `parser.parse()` method will always contain the total number of required and optional arguments that the method can take, with unspecified arguments given by empty matrices. Thus, to check if an optional argument has been given, you can do:
```cpp
int dtype = inputs[3].empty() ? -1 : inputs[3].scalar<double>();
```
**bridge.hpp**
The bridge interface defines a `Bridge` class which provides type conversion between std/OpenCV and Matlab types. A type conversion must provide the following:
......
......@@ -57,6 +57,21 @@
{%- endmacro %}
/*
* composeVariant
* compose a variant call for the ArgumentParser
*/
{% macro composeVariant(fun) %}
addVariant("{{ fun.name }}", {{ fun.req|inputs|length }}, {{ fun.opt|inputs|length }}
{%- if fun.opt|inputs|length %}, {% endif -%}
{%- for arg in fun.opt|inputs -%}
"{{arg.name}}"
{%- if not loop.last %}, {% endif -%}
{% endfor -%}
)
{%- endmacro %}
/*
* composeWithExceptionHandler
* compose a function call wrapped in exception traps
......
......@@ -30,18 +30,20 @@ using namespace bridge;
void mexFunction(int nlhs, mxArray*{% if fun|noutputs %} plhs[]{% else %}*{% endif %},
int nrhs, const mxArray*{% if fun|ninputs %} prhs[]{% else %}*{% endif %}) {
// assertions
conditionalError(nrhs >= {{fun.req|length - fun.req|only|outputs|length}}, "Too few required input arguments specified");
conditionalError(nrhs <= {{fun.req|length + fun.opt|length - fun.req|only|outputs|length - fun.opt|only|outputs|length}}, "Too many input arguments specified");
conditionalError(nlhs <= {{ fun.rtp|void|not + fun.req|outputs|length + fun.opt|outputs|length}}, "Too many output arguments specified");
{% if fun|ninputs %}
// parse the inputs
ArgumentParser parser("{{fun.name}}");
parser.{{ functional.composeVariant(fun) }};
MxArrayVector sorted = parser.parse(MxArrayVector(prhs, prhs+nrhs));
{%endif %}
{% if fun|ninputs or fun|noutputs %}
// setup
{% if fun|ninputs %}
std::vector<Bridge> inputs(prhs, prhs+nrhs);
BridgeVector inputs(sorted.begin(), sorted.end());
{% endif -%}
{%- if fun|noutputs %}
std::vector<Bridge> outputs({{fun|noutputs}});
BridgeVector outputs({{fun|noutputs}});
{% endif %}
{% endif %}
......
......@@ -73,6 +73,7 @@ typedef cv::Ptr<cv::FeatureDetector> Ptr_FeatureDetector;
// PREDECLARATIONS
// ----------------------------------------------------------------------------
class Bridge;
typedef std::vector<Bridge> BridgeVector;
template <typename InputScalar, typename OutputScalar>
void deepCopyAndTranspose(const cv::Mat& src, matlab::MxArray& dst);
......
......@@ -75,6 +75,12 @@ extern "C" {
#endif
namespace matlab {
// ----------------------------------------------------------------------------
// PREDECLARATIONS
// ----------------------------------------------------------------------------
class MxArray;
typedef std::vector<MxArray> MxArrayVector;
/*!
* @brief raise error if condition fails
*
......@@ -476,7 +482,6 @@ private:
typedef std::string String;
typedef std::vector<std::string> StringVector;
typedef std::vector<size_t> IndexVector;
typedef std::vector<MxArray> MxArrayVector;
typedef std::vector<Variant> VariantVector;
/* @class Variant
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment