mirror of
https://github.com/opencv/opencv.git
synced 2024-12-17 02:48:01 +08:00
685 lines
25 KiB
C++
685 lines
25 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
|
//
|
|
// By downloading, copying, installing or using the software you agree to this
|
|
// license. If you do not agree to this license, do not download, install,
|
|
// copy or use the software.
|
|
//
|
|
//
|
|
// License Agreement
|
|
// For Open Source Computer Vision Library
|
|
//
|
|
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
|
// Third party copyrights are property of their respective owners.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistribution's of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
//
|
|
// * Redistribution's in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
//
|
|
// * The name of the copyright holders may not be used to endorse or promote
|
|
// products derived from this software without specific prior written
|
|
// permission.
|
|
//
|
|
// This software is provided by the copyright holders and contributors "as is"
|
|
// and any express or implied warranties, including, but not limited to, the
|
|
// implied warranties of merchantability and fitness for a particular purpose
|
|
// are disclaimed. In no event shall the Intel Corporation or contributors be
|
|
// liable for any direct, indirect, incidental, special, exemplary, or
|
|
// consequential damages (including, but not limited to, procurement of
|
|
// substitute goods or services; loss of use, data, or profits; or business
|
|
// interruption) however caused and on any theory of liability, whether in
|
|
// contract, strict liability, or tort (including negligence or otherwise)
|
|
// arising in any way out of the use of this software, even if advised of the
|
|
// possibility of such damage.
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
#ifndef OPENCV_MXARRAY_HPP_
|
|
#define OPENCV_MXARRAY_HPP_
|
|
|
|
#include <mex.h>
|
|
#include <stdint.h>
|
|
#include <cstdarg>
|
|
#include <algorithm>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <sstream>
|
|
#if __cplusplus > 201103
|
|
#include <unordered_set>
|
|
typedef std::unordered_set<std::string> StringSet;
|
|
#else
|
|
#include <set>
|
|
typedef std::set<std::string> StringSet;
|
|
#endif
|
|
|
|
/*
|
|
* All recent versions of Matlab ship with the MKL library which contains
|
|
* a blas extension called mkl_?omatcopy(). This defines an out-of-place
|
|
* copy and transpose operation.
|
|
*
|
|
* The mkl library is in ${MATLAB_ROOT}/bin/${MATLAB_MEXEXT}/libmkl...
|
|
* Matlab does not ship headers for the mkl functions, so we define them
|
|
* here.
|
|
*
|
|
*/
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
namespace matlab {
|
|
// ----------------------------------------------------------------------------
|
|
// PREDECLARATIONS
|
|
// ----------------------------------------------------------------------------
|
|
class MxArray;
|
|
typedef std::vector<MxArray> MxArrayVector;
|
|
|
|
/*!
|
|
* @brief raise error if condition fails
|
|
*
|
|
* This is a conditional wrapper for mexErrMsgTxt. If the conditional
|
|
* expression fails, an error is raised and the mex function returns
|
|
* to Matlab, otherwise this function does nothing
|
|
*/
|
|
static void conditionalError(bool expr, const std::string& str) {
|
|
if (!expr) mexErrMsgTxt(std::string("condition failed: ").append(str).c_str());
|
|
}
|
|
|
|
/*!
|
|
* @brief raise an error
|
|
*
|
|
* This function is a wrapper around mexErrMsgTxt
|
|
*/
|
|
static void error(const std::string& str) {
|
|
mexErrMsgTxt(str.c_str());
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MATLAB TRAITS
|
|
// ----------------------------------------------------------------------------
|
|
class DefaultTraits {};
|
|
class InheritType {};
|
|
|
|
template<typename _Tp = DefaultTraits> class Traits {
|
|
public:
|
|
static const mxClassID ScalarType = mxUNKNOWN_CLASS;
|
|
static const mxComplexity Complex = mxCOMPLEX;
|
|
static const mxComplexity Real = mxREAL;
|
|
static std::string ToString() { return "Unknown/Unsupported"; }
|
|
};
|
|
// bool
|
|
template<> class Traits<bool> {
|
|
public:
|
|
static const mxClassID ScalarType = mxLOGICAL_CLASS;
|
|
static std::string ToString() { return "boolean"; }
|
|
};
|
|
// uint8_t
|
|
template<> class Traits<uint8_t> {
|
|
public:
|
|
static const mxClassID ScalarType = mxUINT8_CLASS;
|
|
static std::string ToString() { return "uint8_t"; }
|
|
};
|
|
// int8_t
|
|
template<> class Traits<int8_t> {
|
|
public:
|
|
static const mxClassID ScalarType = mxINT8_CLASS;
|
|
static std::string ToString() { return "int8_t"; }
|
|
};
|
|
// uint16_t
|
|
template<> class Traits<uint16_t> {
|
|
public:
|
|
static const mxClassID ScalarType = mxUINT16_CLASS;
|
|
static std::string ToString() { return "uint16_t"; }
|
|
};
|
|
// int16_t
|
|
template<> class Traits<int16_t> {
|
|
public:
|
|
static const mxClassID ScalarType = mxINT16_CLASS;
|
|
static std::string ToString() { return "int16_t"; }
|
|
};
|
|
// uint32_t
|
|
template<> class Traits<uint32_t> {
|
|
public:
|
|
static const mxClassID ScalarType = mxUINT32_CLASS;
|
|
static std::string ToString() { return "uint32_t"; }
|
|
};
|
|
// int32_t
|
|
template<> class Traits<int32_t> {
|
|
public:
|
|
static const mxClassID ScalarType = mxINT32_CLASS;
|
|
static std::string ToString() { return "int32_t"; }
|
|
};
|
|
// uint64_t
|
|
template<> class Traits<uint64_t> {
|
|
public:
|
|
static const mxClassID ScalarType = mxUINT64_CLASS;
|
|
static std::string ToString() { return "uint64_t"; }
|
|
};
|
|
// int64_t
|
|
template<> class Traits<int64_t> {
|
|
public:
|
|
static const mxClassID ScalarType = mxINT64_CLASS;
|
|
static std::string ToString() { return "int64_t"; }
|
|
};
|
|
// float
|
|
template<> class Traits<float> {
|
|
public:
|
|
static const mxClassID ScalarType = mxSINGLE_CLASS;
|
|
static std::string ToString() { return "float"; }
|
|
};
|
|
// double
|
|
template<> class Traits<double> {
|
|
public:
|
|
static const mxClassID ScalarType = mxDOUBLE_CLASS;
|
|
static std::string ToString() { return "double"; }
|
|
};
|
|
// char
|
|
template<> class Traits<char> {
|
|
public:
|
|
static const mxClassID ScalarType = mxCHAR_CLASS;
|
|
static std::string ToString() { return "char"; }
|
|
};
|
|
// inherited type
|
|
template<> class Traits<matlab::InheritType> {
|
|
public:
|
|
static std::string ToString() { return "Inherited type"; }
|
|
};
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// MXARRAY
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
/*!
|
|
* @class MxArray
|
|
* @brief A thin wrapper around Matlab's mxArray types
|
|
*
|
|
* MxArray provides a thin object oriented wrapper around Matlab's
|
|
* native mxArray type which exposes most of the functionality of the
|
|
* Matlab interface, but in a more C++ manner. MxArray objects are scoped,
|
|
* so you can freely create and destroy them without worrying about memory
|
|
* management. If you wish to pass the underlying mxArray* representation
|
|
* back to Matlab as an lvalue, see the releaseOwnership() method
|
|
*
|
|
* MxArrays can be directly converted into OpenCV mat objects and std::string
|
|
* objects, since there is a natural mapping between these types. More
|
|
* complex types are mapped through the Bridge which does custom conversions
|
|
* such as MxArray --> cv::Keypoints, etc
|
|
*/
|
|
class MxArray {
|
|
private:
|
|
mxArray* ptr_;
|
|
bool owns_;
|
|
|
|
/*!
|
|
* @brief swap all members of this and other
|
|
*
|
|
* the swap method is used by the assignment and move constructors
|
|
* to swap the members of two MxArrays, leaving both in destructible states
|
|
*/
|
|
friend void swap(MxArray& first, MxArray& second) {
|
|
using std::swap;
|
|
swap(first.ptr_, second.ptr_);
|
|
swap(first.owns_, second.owns_);
|
|
}
|
|
|
|
void dealloc() {
|
|
if (owns_ && ptr_) { mxDestroyArray(ptr_); ptr_ = NULL; owns_ = false; }
|
|
}
|
|
public:
|
|
// --------------------------------------------------------------------------
|
|
// CONSTRUCTORS
|
|
// --------------------------------------------------------------------------
|
|
/*!
|
|
* @brief default constructor
|
|
*
|
|
* Construct a valid 0x0 matrix (so all other methods do not need validity checks)
|
|
*/
|
|
MxArray() : ptr_(mxCreateDoubleMatrix(0, 0, matlab::Traits<>::Real)), owns_(true) {}
|
|
|
|
/*!
|
|
* @brief destructor
|
|
*
|
|
* The destructor deallocates any data allocated by mxCreate* methods only
|
|
* if the object is owned
|
|
*/
|
|
virtual ~MxArray() {
|
|
dealloc();
|
|
}
|
|
|
|
/*!
|
|
* @brief inheriting constructor
|
|
*
|
|
* Inherit an mxArray from Matlab. Don't claim ownership of the array,
|
|
* just encapsulate it
|
|
*/
|
|
MxArray(const mxArray* ptr) : ptr_(const_cast<mxArray *>(ptr)), owns_(false) {}
|
|
MxArray& operator=(const mxArray* ptr) {
|
|
dealloc();
|
|
ptr_ = const_cast<mxArray *>(ptr);
|
|
owns_ = false;
|
|
return *this;
|
|
}
|
|
|
|
/*!
|
|
* @brief explicit typed constructor
|
|
*
|
|
* This constructor explicitly creates an MxArray of the given size and type.
|
|
*/
|
|
MxArray(size_t m, size_t n, size_t k, mxClassID id, mxComplexity com = matlab::Traits<>::Real)
|
|
: ptr_(NULL), owns_(true) {
|
|
mwSize dims[] = { static_cast<mwSize>(m), static_cast<mwSize>(n), static_cast<mwSize>(k) };
|
|
ptr_ = mxCreateNumericArray(3, dims, id, com);
|
|
}
|
|
|
|
/*!
|
|
* @brief explicit tensor constructor
|
|
*
|
|
* Explicitly construct a tensor of given size and type. Since constructors cannot
|
|
* be explicitly templated, this is a static factory method
|
|
*/
|
|
template <typename Scalar>
|
|
static MxArray Tensor(size_t m, size_t n, size_t k=1) {
|
|
return MxArray(m, n, k, matlab::Traits<Scalar>::ScalarType);
|
|
}
|
|
|
|
/*!
|
|
* @brief explicit matrix constructor
|
|
*
|
|
* Explicitly construct a matrix of given size and type. Since constructors cannot
|
|
* be explicitly templated, this is a static factory method
|
|
*/
|
|
template <typename Scalar>
|
|
static MxArray Matrix(size_t m, size_t n) {
|
|
return MxArray(m, n, 1, matlab::Traits<Scalar>::ScalarType);
|
|
}
|
|
|
|
/*!
|
|
* @brief explicit vector constructor
|
|
*
|
|
* Explicitly construct a vector of given size and type. Since constructors cannot
|
|
* be explicitly templated, this is a static factory method
|
|
*/
|
|
template <typename Scalar>
|
|
static MxArray Vector(size_t m) {
|
|
return MxArray(m, 1, 1, matlab::Traits<Scalar>::ScalarType);
|
|
}
|
|
|
|
/*!
|
|
* @brief explicit scalar constructor
|
|
*
|
|
* Explicitly construct a scalar of given type. Since constructors cannot
|
|
* be explicitly templated, this is a static factory method
|
|
*/
|
|
template <typename ScalarType>
|
|
static MxArray Scalar(ScalarType value = 0) {
|
|
MxArray s(1, 1, 1, matlab::Traits<ScalarType>::ScalarType);
|
|
s.real<ScalarType>()[0] = value;
|
|
return s;
|
|
}
|
|
|
|
/*!
|
|
* @brief copy constructor
|
|
*
|
|
* All copies are deep copies. If you have a C++11 compatible compiler, prefer
|
|
* move construction to copy construction
|
|
*/
|
|
MxArray(const MxArray& other) : ptr_(mxDuplicateArray(other.ptr_)), owns_(true) {}
|
|
|
|
/*!
|
|
* @brief copy-and-swap assignment
|
|
*
|
|
* This assignment operator uses the copy and swap idiom to provide a strong
|
|
* exception guarantee when swapping two objects.
|
|
*
|
|
* Note in particular that the other MxArray is passed by value, thus invoking
|
|
* the copy constructor which performs a deep copy of the input. The members of
|
|
* this and other are then swapped
|
|
*/
|
|
MxArray& operator=(MxArray other) {
|
|
swap(*this, other);
|
|
return *this;
|
|
}
|
|
#if __cplusplus >= 201103L
|
|
/*
|
|
* @brief C++11 move constructor
|
|
*
|
|
* When C++11 support is available, move construction is used to move returns
|
|
* out of functions, etc. This is much fast than copy construction, since the
|
|
* move constructed object replaced itself with a default constructed MxArray,
|
|
* which is of size 0 x 0.
|
|
*/
|
|
MxArray(MxArray&& other) : MxArray() {
|
|
swap(*this, other);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* @brief release ownership to allow return into Matlab workspace
|
|
*
|
|
* MxArray is not directly convertible back to mxArray types through assignment
|
|
* because the MxArray may have been allocated on the free store, making it impossible
|
|
* to know whether the returned pointer will be released by someone else or not.
|
|
*
|
|
* Since Matlab requires mxArrays be passed back into the workspace, the only way
|
|
* to achieve that is through this function, which explicitly releases ownership
|
|
* of the object, assuming the Matlab interpreter receving the object will delete
|
|
* it at a later time
|
|
*
|
|
* e.g.
|
|
* {
|
|
* MxArray A = MxArray::Matrix<double>(5, 5); // allocates memory
|
|
* MxArray B = MxArray::Matrix<double>(5, 5); // ditto
|
|
* plhs[0] = A; // not allowed!!
|
|
* plhs[0] = A.releaseOwnership(); // makes explicit that ownership is being released
|
|
* } // end of scope. B is released, A isn't
|
|
*
|
|
*/
|
|
mxArray* releaseOwnership() {
|
|
owns_ = false;
|
|
return ptr_;
|
|
}
|
|
|
|
MxArray field(const std::string& name) { return MxArray(mxGetField(ptr_, 0, name.c_str())); }
|
|
|
|
template <typename Scalar>
|
|
Scalar* real() { return static_cast<Scalar *>(mxGetData(ptr_)); }
|
|
|
|
template <typename Scalar>
|
|
Scalar* imag() { return static_cast<Scalar *>(mxGetImagData(ptr_)); }
|
|
|
|
template <typename Scalar>
|
|
const Scalar* real() const { return static_cast<const Scalar *>(mxGetData(ptr_)); }
|
|
|
|
template <typename Scalar>
|
|
const Scalar* imag() const { return static_cast<const Scalar *>(mxGetData(ptr_)); }
|
|
|
|
template <typename Scalar>
|
|
Scalar scalar() const { return static_cast<Scalar *>(mxGetData(ptr_))[0]; }
|
|
|
|
std::string toString() const {
|
|
conditionalError(isString(), "Attempted to convert non-string type to string");
|
|
std::string str(size(), '\0');
|
|
mxGetString(ptr_, const_cast<char *>(str.data()), str.size()+1);
|
|
return str;
|
|
}
|
|
|
|
size_t size() const { return mxGetNumberOfElements(ptr_); }
|
|
bool empty() const { return size() == 0; }
|
|
size_t rows() const { return mxGetDimensions(ptr_)[0]; }
|
|
size_t cols() const { return mxGetDimensions(ptr_)[1]; }
|
|
size_t channels() const { return (mxGetNumberOfDimensions(ptr_) > 2) ? mxGetDimensions(ptr_)[2] : 1; }
|
|
bool isComplex() const { return mxIsComplex(ptr_); }
|
|
bool isNumeric() const { return mxIsNumeric(ptr_); }
|
|
bool isLogical() const { return mxIsLogical(ptr_); }
|
|
bool isString() const { return mxIsChar(ptr_); }
|
|
bool isCell() const { return mxIsCell(ptr_); }
|
|
bool isStructure() const { return mxIsStruct(ptr_); }
|
|
bool isClass(const std::string& name) const { return mxIsClass(ptr_, name.c_str()); }
|
|
std::string className() const { return std::string(mxGetClassName(ptr_)); }
|
|
mxClassID ID() const { return mxGetClassID(ptr_); }
|
|
|
|
};
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// ARGUMENT PARSER
|
|
// ----------------------------------------------------------------------------
|
|
|
|
/*! @class ArgumentParser
|
|
* @brief parses inputs to a method and resolves the argument names.
|
|
*
|
|
* The ArgumentParser resolves the inputs to a method. It checks that all
|
|
* required arguments are specified and also allows named optional arguments.
|
|
* For example, the C++ function:
|
|
* void randn(Mat& mat, Mat& mean=Mat(), Mat& std=Mat());
|
|
* could be called in Matlab using any of the following signatures:
|
|
* \code
|
|
* out = randn(in);
|
|
* out = randn(in, 0, 1);
|
|
* out = randn(in, 'mean', 0, 'std', 1);
|
|
* \endcode
|
|
*
|
|
* ArgumentParser also enables function overloading by allowing users
|
|
* to add variants to a method. For example, there may be two C++ sum() methods:
|
|
* \code
|
|
* double sum(Mat& mat); % sum elements of a matrix
|
|
* Mat sum(Mat& A, Mat& B); % add two matrices
|
|
* \endcode
|
|
*
|
|
* by adding two variants to ArgumentParser, the correct underlying sum
|
|
* method can be called. If the function call is ambiguous, the
|
|
* ArgumentParser will fail with an error message.
|
|
*
|
|
* The previous example could be parsed as:
|
|
* \code
|
|
* // set up the Argument parser
|
|
* ArgumentParser arguments;
|
|
* arguments.addVariant("elementwise", 1);
|
|
* arguments.addVariant("matrix", 2);
|
|
*
|
|
* // parse the arguments
|
|
* std::vector<MxArray> inputs;
|
|
* inputs = arguments.parse(std::vector<MxArray>(prhs, prhs+nrhs));
|
|
*
|
|
* // if we get here, one unique variant is valid
|
|
* if (arguments.variantIs("elementwise")) {
|
|
* // call elementwise sum()
|
|
* }
|
|
* \endcode
|
|
*/
|
|
class ArgumentParser {
|
|
private:
|
|
struct Variant;
|
|
typedef std::string String;
|
|
typedef std::vector<std::string> StringVector;
|
|
typedef std::vector<size_t> IndexVector;
|
|
typedef std::vector<Variant> VariantVector;
|
|
|
|
/* @class Variant
|
|
* @brief Describes a variant of arguments to a method
|
|
*
|
|
* When addVariant() is called on an instance to ArgumentParser, this class
|
|
* holds the the information that decribes that variant. The parse() method
|
|
* of ArgumentParser then attempts to match a Variant, given a set of
|
|
* inputs for a method invocation.
|
|
*/
|
|
class Variant {
|
|
private:
|
|
String name_;
|
|
size_t Nreq_;
|
|
size_t Nopt_;
|
|
StringVector keys_;
|
|
IndexVector order_;
|
|
bool valid_;
|
|
size_t nparsed_;
|
|
size_t nkeys_;
|
|
size_t working_opt_;
|
|
bool expecting_val_;
|
|
bool using_named_;
|
|
size_t find(const String& key) const {
|
|
return std::find(keys_.begin(), keys_.end(), key) - keys_.begin();
|
|
}
|
|
public:
|
|
/*! @brief default constructor */
|
|
Variant() : Nreq_(0), Nopt_(0), valid_(false) {}
|
|
/*! @brief construct a new variant spec */
|
|
Variant(const String& name, size_t Nreq, size_t Nopt, const StringVector& keys)
|
|
: name_(name), Nreq_(Nreq), Nopt_(Nopt), keys_(keys),
|
|
order_(Nreq+Nopt, Nreq+2*Nopt), valid_(true), nparsed_(0), nkeys_(0),
|
|
working_opt_(0), expecting_val_(false), using_named_(false) {}
|
|
/*! @brief the name of the variant */
|
|
String name() const { return name_; }
|
|
/*! @brief return the total number of arguments the variant can take */
|
|
size_t size() const { return Nreq_ + Nopt_; }
|
|
/*! @brief has the variant been fulfilled? */
|
|
bool fulfilled() const { return (valid_ && nparsed_ >= Nreq_ && !expecting_val_); }
|
|
/*! @brief is the variant in a valid state (though not necessarily fulfilled) */
|
|
bool valid() const { return valid_; }
|
|
/*! @brief check if the named argument exists in the variant */
|
|
bool exist(const String& key) const { return find(key) != keys_.size(); }
|
|
/*! @brief retrieve the order mapping raw inputs to their position in the variant */
|
|
const IndexVector& order() const { return order_; }
|
|
size_t order(size_t n) const { return order_[n]; }
|
|
/*! @brief attempt to parse the next argument as a value */
|
|
bool parseNextAsValue() {
|
|
if (!valid_) {}
|
|
else if ((using_named_ && !expecting_val_) || (nparsed_-nkeys_ == Nreq_+Nopt_)) { valid_ = false; }
|
|
else if (nparsed_ < Nreq_) { order_[nparsed_] = nparsed_; }
|
|
else if (!using_named_) { order_[nparsed_] = nparsed_; }
|
|
else if (using_named_ && expecting_val_) { order_[Nreq_ + working_opt_] = nparsed_; }
|
|
nparsed_++;
|
|
expecting_val_ = false;
|
|
return valid_;
|
|
}
|
|
/*! @biref attempt to parse the next argument as a name (key) */
|
|
bool parseNextAsKey(const String& key) {
|
|
if (!valid_) {}
|
|
else if ((nparsed_ < Nreq_) || (nparsed_-nkeys_ == Nreq_+Nopt_)) { valid_ = false; }
|
|
else if (using_named_ && expecting_val_) { valid_ = false; }
|
|
else if ((working_opt_ = find(key)) == keys_.size()) { valid_ = false; }
|
|
else { using_named_ = true; expecting_val_ = true; nkeys_++; nparsed_++; }
|
|
return valid_;
|
|
}
|
|
String toString(const String& method_name="f") const {
|
|
int req_begin = 0, req_end = 0, opt_begin = 0, opt_end = 0;
|
|
std::ostringstream s;
|
|
// f(...)
|
|
s << method_name << "(";
|
|
// required arguments
|
|
req_begin = s.str().size();
|
|
for (size_t n = 0; n < Nreq_; ++n) { s << "src" << n+1 << (n != Nreq_-1 ? ", " : ""); }
|
|
req_end = s.str().size();
|
|
if (Nreq_ && Nopt_) s << ", ";
|
|
// optional arguments
|
|
opt_begin = s.str().size();
|
|
for (size_t n = 0; n < keys_.size(); ++n) { s << "'" << keys_[n] << "', " << keys_[n] << (n != Nopt_-1 ? ", " : ""); }
|
|
opt_end = s.str().size();
|
|
s << ");";
|
|
if (Nreq_ + Nopt_ == 0) return s.str();
|
|
// underscores
|
|
String under = String(req_begin, ' ') + String(req_end-req_begin, '-')
|
|
+ String(std::max(opt_begin-req_end,0), ' ') + String(opt_end-opt_begin, '-');
|
|
s << "\n" << under;
|
|
// required and optional sets
|
|
String req_set(req_end-req_begin, ' ');
|
|
String opt_set(opt_end-opt_begin, ' ');
|
|
if (!req_set.empty() && req_set.size() < 8) req_set.replace((req_set.size()-3)/2, 3, "req");
|
|
if (req_set.size() > 7) req_set.replace((req_set.size()-8)/2, 8, "required");
|
|
if (!opt_set.empty() && opt_set.size() < 8) opt_set.replace((opt_set.size()-3)/2, 3, "opt");
|
|
if (opt_set.size() > 7) opt_set.replace((opt_set.size()-8)/2, 8, "optional");
|
|
String set = String(req_begin, ' ') + req_set + String(std::max(opt_begin-req_end,0), ' ') + opt_set;
|
|
s << "\n" << set;
|
|
return s.str();
|
|
}
|
|
};
|
|
/*! @brief given an input and output vector of arguments, and a variant spec, sort */
|
|
void sortArguments(Variant& v, MxArrayVector& in, MxArrayVector& out) {
|
|
// allocate the output array with ALL arguments
|
|
out.resize(v.size());
|
|
// reorder the inputs based on the variant ordering
|
|
for (size_t n = 0; n < v.size(); ++n) {
|
|
if (v.order(n) >= in.size()) continue;
|
|
swap(in[v.order(n)], out[n]);
|
|
}
|
|
}
|
|
VariantVector variants_;
|
|
String valid_;
|
|
String method_name_;
|
|
public:
|
|
ArgumentParser(const String& method_name) : method_name_(method_name) {}
|
|
|
|
/*! @brief add a function call variant to the parser
|
|
*
|
|
* Adds a function-call signature to the parser. The function call *must* be
|
|
* unique either in its number of arguments, or in the named-syntax.
|
|
* Currently this function does not check whether that invariant stands true.
|
|
*
|
|
* This function is variadic. If should be called as follows:
|
|
* addVariant(2, 2, 'opt_1_name', 'opt_2_name');
|
|
*/
|
|
void addVariant(const String& name, size_t nreq, size_t nopt = 0, ...) {
|
|
StringVector keys;
|
|
va_list opt;
|
|
va_start(opt, nopt);
|
|
for (size_t n = 0; n < nopt; ++n) keys.push_back(va_arg(opt, const char*));
|
|
addVariant(name, nreq, nopt, keys);
|
|
}
|
|
void addVariant(const String& name, size_t nreq, size_t nopt, StringVector keys) {
|
|
variants_.push_back(Variant(name, nreq, nopt, keys));
|
|
}
|
|
|
|
/*! @brief check if the valid variant is the key name */
|
|
bool variantIs(const String& name) {
|
|
return name.compare(valid_) == 0;
|
|
}
|
|
|
|
/*! @brief parse a vector of input arguments
|
|
*
|
|
* This method parses a vector of input arguments, attempting to match them
|
|
* to a Variant spec. For each input, the method attempts to cull any
|
|
* Variants which don't match the given inputs so far.
|
|
*
|
|
* Once all inputs have been parsed, if there is one unique spec remaining,
|
|
* the output MxArray vector gets populated with the arguments, with named
|
|
* arguments removed. Any optional arguments that have not been encountered
|
|
* are set to an empty array.
|
|
*
|
|
* If multiple variants or no variants match the given call, an error
|
|
* message is emitted
|
|
*/
|
|
MxArrayVector parse(const MxArrayVector& inputs) {
|
|
// allocate the outputs
|
|
String variant_string;
|
|
MxArrayVector outputs;
|
|
VariantVector candidates = variants_;
|
|
|
|
// iterate over the inputs, attempting to match a variant
|
|
for (MxArrayVector::const_iterator input = inputs.begin(); input != inputs.end(); ++input) {
|
|
String name = input->isString() ? input->toString() : String();
|
|
for (VariantVector::iterator candidate = candidates.begin(); candidate < candidates.end(); ++candidate) {
|
|
candidate->exist(name) ? candidate->parseNextAsKey(name) : candidate->parseNextAsValue();
|
|
}
|
|
}
|
|
|
|
// make sure the candidates have been fulfilled
|
|
for (VariantVector::iterator candidate = candidates.begin(); candidate < candidates.end(); ++candidate) {
|
|
if (!candidate->fulfilled()) candidate = candidates.erase(candidate)--;
|
|
}
|
|
|
|
// if there is not a unique candidate, throw an error
|
|
for (VariantVector::iterator variant = variants_.begin(); variant != variants_.end(); ++variant) {
|
|
variant_string += "\n" + variant->toString(method_name_);
|
|
}
|
|
|
|
// if there is not a unique candidate, throw an error
|
|
if (candidates.size() > 1) {
|
|
error(String("Call to method is ambiguous. Valid variants are:")
|
|
.append(variant_string).append("\nUse named arguments to disambiguate call"));
|
|
}
|
|
if (candidates.size() == 0) {
|
|
error(String("No matching method signatures for given arguments. Valid variants are:").append(variant_string));
|
|
}
|
|
|
|
// Unique candidate!
|
|
valid_ = candidates[0].name();
|
|
sortArguments(candidates[0], const_cast<MxArrayVector&>(inputs), outputs);
|
|
return outputs;
|
|
}
|
|
};
|
|
|
|
} // namespace matlab
|
|
|
|
#endif
|