Added custom OpenCV mex compiler
This commit is contained in:
parent
ef2c1e1a24
commit
52dc51a62c
@ -34,21 +34,30 @@ macro(PREPEND TOKEN OUT IN)
|
|||||||
endforeach()
|
endforeach()
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
|
||||||
# WARN_MIXED_PRECISION
|
# WARN_MIXED_PRECISION
|
||||||
# Formats a warning message if the compiler and Matlab bitness is different
|
# Formats a warning message if the compiler and Matlab bitness is different
|
||||||
macro(WARN_MIXED_PRECISION COMPILER_BITNESS MATLAB_BITNESS)
|
macro(WARN_MIXED_PRECISION COMPILER_BITNESS MATLAB_BITNESS)
|
||||||
set(MSG "Your compiler is ${COMPILER_BITNESS}-bit")
|
set(MSG "Your compiler is ${COMPILER_BITNESS}-bit")
|
||||||
set(MSG "${MSG} but your version of Matlab is ${MATLAB_BITNESS}-bit.")
|
set(MSG "${MSG} but your version of Matlab is ${MATLAB_BITNESS}-bit.")
|
||||||
set(MSG "${MSG} Mixed preicision pointers are not supported. Disabling Matlab bindings...")
|
set(MSG "${MSG} To build Matlab bindings, please switch to a ${MATLAB_BITNESS}-bit compiler.")
|
||||||
message(WARNING ${MSG})
|
message(WARNING ${MSG})
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Architecture checks
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
# make sure we're on a supported architecture with Matlab and python installed
|
# make sure we're on a supported architecture with Matlab and python installed
|
||||||
if (IOS OR ANDROID OR NOT MATLAB_FOUND OR NOT PYTHONLIBS_FOUND)
|
if (IOS OR ANDROID OR NOT MATLAB_FOUND)
|
||||||
|
ocv_module_disable(matlab)
|
||||||
|
return()
|
||||||
|
elseif (NOT PYTHONLIBS_FOUND)
|
||||||
|
message(WARNING "A required dependency of the matlab module (PythonLibs) was not found. Disabling Matlab bindings...")
|
||||||
ocv_module_disable(matlab)
|
ocv_module_disable(matlab)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# If the user built OpenCV as X-bit, but they have a Y-bit version of Matlab,
|
# If the user built OpenCV as X-bit, but they have a Y-bit version of Matlab,
|
||||||
# attempting to link to OpenCV during binding generation will fail, since
|
# attempting to link to OpenCV during binding generation will fail, since
|
||||||
# mixed precision pointers are not allowed. Disable the bindings.
|
# mixed precision pointers are not allowed. Disable the bindings.
|
||||||
@ -63,6 +72,16 @@ elseif (${ARCH} EQUAL 64 AND NOT ${MATLAB_ARCH} MATCHES "64")
|
|||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# If it's MSVC, warn the user that bindings will only be built in Release mode.
|
||||||
|
# Debug mode seems to cause issues...
|
||||||
|
if (MSVC)
|
||||||
|
message(STATUS "Warning: Matlab bindings will only be built in Release configurations")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Configure time components
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
set(the_description "The Matlab/Octave bindings")
|
set(the_description "The Matlab/Octave bindings")
|
||||||
ocv_add_module(matlab BINDINGS
|
ocv_add_module(matlab BINDINGS
|
||||||
OPTIONAL opencv_core
|
OPTIONAL opencv_core
|
||||||
@ -93,9 +112,9 @@ endif()
|
|||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# intersection of available modules and optional dependencies
|
||||||
# Configure time components
|
# 1. populate the command-line include directories (-I/path/to/module/header, ...)
|
||||||
# ----------------------------------------------------------------------------
|
# 2. populate the command-line link libraries (-lopencv_core, ...) for Debug and Release
|
||||||
set(MATLAB_DEPS ${OPENCV_MODULE_${the_module}_REQ_DEPS} ${OPENCV_MODULE_${the_module}_OPT_DEPS})
|
set(MATLAB_DEPS ${OPENCV_MODULE_${the_module}_REQ_DEPS} ${OPENCV_MODULE_${the_module}_OPT_DEPS})
|
||||||
foreach(opencv_module ${MATLAB_DEPS})
|
foreach(opencv_module ${MATLAB_DEPS})
|
||||||
if (HAVE_${opencv_module})
|
if (HAVE_${opencv_module})
|
||||||
@ -114,7 +133,7 @@ list(APPEND opencv_extra_hdrs "core=${OPENCV_MODULE_opencv_core_LOCATION}/includ
|
|||||||
# pass the OPENCV_CXX_EXTRA_FLAGS through to the mex compiler
|
# pass the OPENCV_CXX_EXTRA_FLAGS through to the mex compiler
|
||||||
# remove the visibility modifiers, so the mex gateway is visible
|
# remove the visibility modifiers, so the mex gateway is visible
|
||||||
# TODO: get mex working without warnings
|
# TODO: get mex working without warnings
|
||||||
#string(REGEX REPLACE "[^\ ]*visibility[^\ ]*" "" MEX_CXXFLAGS "${OPENCV_EXTRA_FLAGS} ${OPENCV_EXTRA_CXX_FLAGS}")
|
string(REGEX REPLACE "[^\ ]*visibility[^\ ]*" "" MEX_CXXFLAGS "${OPENCV_EXTRA_FLAGS} ${OPENCV_EXTRA_CXX_FLAGS}")
|
||||||
|
|
||||||
# Configure checks
|
# Configure checks
|
||||||
# Check to see whether the generator and the mex compiler are working.
|
# Check to see whether the generator and the mex compiler are working.
|
||||||
@ -149,7 +168,7 @@ if (NOT MEX_WORKS)
|
|||||||
# attempt to compile a gateway using mex
|
# attempt to compile a gateway using mex
|
||||||
message(STATUS "Trying to compile mex file")
|
message(STATUS "Trying to compile mex file")
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND ${MATLAB_MEX_SCRIPT} ${MEX_OPTS} "CXXFLAGS=\$CXXFLAGS ${MEX_CXXFLAGS}"
|
COMMAND ${MATLAB_MEX_SCRIPT} ${MEX_OPTS} "CXXFLAGS=\$CXXFLAGS ${MEX_CXX_FLAGS}"
|
||||||
${MEX_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_compiler.cpp
|
${MEX_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/test/test_compiler.cpp
|
||||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/junk
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/junk
|
||||||
ERROR_VARIABLE MEX_ERROR
|
ERROR_VARIABLE MEX_ERROR
|
||||||
@ -208,6 +227,14 @@ add_custom_command(
|
|||||||
--modules ${opencv_modules}
|
--modules ${opencv_modules}
|
||||||
--configuration "$(Configuration)" ${CMAKE_BUILD_TYPE}
|
--configuration "$(Configuration)" ${CMAKE_BUILD_TYPE}
|
||||||
--outdir ${CMAKE_CURRENT_BINARY_DIR}
|
--outdir ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
COMMAND ${PYTHON_EXECUTABLE}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/generator/cvmex.py
|
||||||
|
--opts="${MEX_OPTS}"
|
||||||
|
--include_dirs="${MEX_INCLUDE_DIRS}"
|
||||||
|
--lib_dir=${MEX_LIB_DIR}
|
||||||
|
--libs="${MEX_LIBS}"
|
||||||
|
--flags ${MEX_CXXFLAGS}
|
||||||
|
--outdir ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/test/help.m ${CMAKE_CURRENT_BINARY_DIR}/+cv
|
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/test/help.m ${CMAKE_CURRENT_BINARY_DIR}/+cv
|
||||||
COMMAND ${CMAKE_COMMAND} -E touch ${GENERATE_PROXY}
|
COMMAND ${CMAKE_COMMAND} -E touch ${GENERATE_PROXY}
|
||||||
COMMENT "Generating Matlab source files"
|
COMMENT "Generating Matlab source files"
|
||||||
@ -222,7 +249,7 @@ add_custom_command(
|
|||||||
COMMAND ${CMAKE_COMMAND} -DMATLAB_MEX_SCRIPT=${MATLAB_MEX_SCRIPT}
|
COMMAND ${CMAKE_COMMAND} -DMATLAB_MEX_SCRIPT=${MATLAB_MEX_SCRIPT}
|
||||||
-DMATLAB_MEXEXT=${MATLAB_MEXEXT}
|
-DMATLAB_MEXEXT=${MATLAB_MEXEXT}
|
||||||
-DMEX_OPTS=${MEX_OPTS}
|
-DMEX_OPTS=${MEX_OPTS}
|
||||||
-DMEX_CXXFLAGS=${MEX_CXXFLAGS}
|
-DMEX_CXXFLAGS=${MEX_CXX_FLAGS}
|
||||||
-DMEX_INCLUDE_DIRS="${MEX_INCLUDE_DIRS}"
|
-DMEX_INCLUDE_DIRS="${MEX_INCLUDE_DIRS}"
|
||||||
-DMEX_LIB_DIR=${MEX_LIB_DIR}
|
-DMEX_LIB_DIR=${MEX_LIB_DIR}
|
||||||
-DCONFIGURATION="$(Configuration)"
|
-DCONFIGURATION="$(Configuration)"
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
|
# LISTIFY
|
||||||
|
# Given a string of space-delimited tokens, reparse as a string of
|
||||||
|
# semi-colon delimited tokens, which in CMake land is exactly equivalent
|
||||||
|
# to a list
|
||||||
macro(listify OUT_LIST IN_STRING)
|
macro(listify OUT_LIST IN_STRING)
|
||||||
string(REPLACE " " ";" ${OUT_LIST} ${IN_STRING})
|
string(REPLACE " " ";" ${OUT_LIST} ${IN_STRING})
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
|
# listify multiple-argument inputs
|
||||||
listify(MEX_INCLUDE_DIRS_LIST ${MEX_INCLUDE_DIRS})
|
listify(MEX_INCLUDE_DIRS_LIST ${MEX_INCLUDE_DIRS})
|
||||||
if (${CONFIGURATION} MATCHES "Debug")
|
if (${CONFIGURATION} MATCHES "Debug")
|
||||||
listify(MEX_LIBS_LIST ${MEX_DEBUG_LIBS})
|
listify(MEX_LIBS_LIST ${MEX_DEBUG_LIBS})
|
||||||
@ -9,11 +14,22 @@ else()
|
|||||||
listify(MEX_LIBS_LIST ${MEX_LIBS})
|
listify(MEX_LIBS_LIST ${MEX_LIBS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# if it's MSVC building a Debug configuration, don't build bindings
|
||||||
|
if ("${CONFIGURATION}" MATCHES "Debug")
|
||||||
|
message(STATUS "Matlab bindings are only available in Release configurations. Skipping...")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# for each generated source file:
|
||||||
|
# 1. check if the file has already been compiled
|
||||||
|
# 2. attempt compile if required
|
||||||
|
# 3. if the compile fails, throw an error and cancel compilation
|
||||||
file(GLOB SOURCE_FILES "${CMAKE_CURRENT_BINARY_DIR}/src/*.cpp")
|
file(GLOB SOURCE_FILES "${CMAKE_CURRENT_BINARY_DIR}/src/*.cpp")
|
||||||
foreach(SOURCE_FILE ${SOURCE_FILES})
|
foreach(SOURCE_FILE ${SOURCE_FILES})
|
||||||
# strip out the filename
|
# strip out the filename
|
||||||
get_filename_component(FILENAME ${SOURCE_FILE} NAME_WE)
|
get_filename_component(FILENAME ${SOURCE_FILE} NAME_WE)
|
||||||
# compie the source file using mex
|
# compile the source file using mex
|
||||||
|
execute_process(COMMAND echo ${FILENAME})
|
||||||
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/+cv/${FILENAME}.${MATLAB_MEXEXT})
|
if (NOT EXISTS ${CMAKE_CURRENT_BINARY_DIR}/+cv/${FILENAME}.${MATLAB_MEXEXT})
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND ${MATLAB_MEX_SCRIPT} ${MEX_OPTS} "CXXFLAGS=\$CXXFLAGS ${MEX_CXXFLAGS}" ${MEX_INCLUDE_DIRS_LIST}
|
COMMAND ${MATLAB_MEX_SCRIPT} ${MEX_OPTS} "CXXFLAGS=\$CXXFLAGS ${MEX_CXXFLAGS}" ${MEX_INCLUDE_DIRS_LIST}
|
||||||
|
@ -19,7 +19,7 @@ def substitute(build, output_dir):
|
|||||||
os.mkdir(output_dir)
|
os.mkdir(output_dir)
|
||||||
|
|
||||||
# populate template
|
# populate template
|
||||||
populated = template.render(build=build)
|
populated = template.render(build=build, time=time)
|
||||||
with open(os.path.join(output_dir, 'buildInformation.m'), 'wb') as f:
|
with open(os.path.join(output_dir, 'buildInformation.m'), 'wb') as f:
|
||||||
f.write(populated)
|
f.write(populated)
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ if __name__ == "__main__":
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# parse the input options
|
# parse the input options
|
||||||
import sys, re, os
|
import sys, re, os, time
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
parser = ArgumentParser()
|
parser = ArgumentParser()
|
||||||
parser.add_argument('--os')
|
parser.add_argument('--os')
|
||||||
|
58
modules/matlab/generator/cvmex.py
Normal file
58
modules/matlab/generator/cvmex.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
def substitute(cv, output_dir):
|
||||||
|
|
||||||
|
# setup the template engine
|
||||||
|
template_dir = os.path.join(os.path.dirname(__file__), 'templates')
|
||||||
|
jtemplate = Environment(loader=FileSystemLoader(template_dir), trim_blocks=True, lstrip_blocks=True)
|
||||||
|
|
||||||
|
# add the filters
|
||||||
|
jtemplate.filters['cellarray'] = cellarray
|
||||||
|
jtemplate.filters['split'] = split
|
||||||
|
jtemplate.filters['csv'] = csv
|
||||||
|
|
||||||
|
# load the template
|
||||||
|
template = jtemplate.get_template('template_cvmex_base.m')
|
||||||
|
|
||||||
|
# create the build directory
|
||||||
|
output_dir = output_dir+'/+cv'
|
||||||
|
if not os.path.isdir(output_dir):
|
||||||
|
os.mkdir(output_dir)
|
||||||
|
|
||||||
|
# populate template
|
||||||
|
populated = template.render(cv=cv, time=time)
|
||||||
|
with open(os.path.join(output_dir, 'mex.m'), 'wb') as f:
|
||||||
|
f.write(populated)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
"""
|
||||||
|
Usage: python cvmex.py --opts [-list -of -opts]
|
||||||
|
--include_dirs [-list -of -opencv_include_directories]
|
||||||
|
--lib_dir opencv_lib_directory
|
||||||
|
--libs [-lopencv_core -lopencv_imgproc ...]
|
||||||
|
--flags [-Wall -opencv_build_flags ...]
|
||||||
|
--outdir /path/to/generated/output
|
||||||
|
|
||||||
|
cvmex.py generates a custom mex compiler that automatically links OpenCV
|
||||||
|
libraries to built sources where appropriate. The calling syntax is the
|
||||||
|
same as the builtin mex compiler, with added cv qualification:
|
||||||
|
>> cv.mex(..., ...);
|
||||||
|
"""
|
||||||
|
|
||||||
|
# parse the input options
|
||||||
|
import sys, re, os, time
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
parser = ArgumentParser()
|
||||||
|
parser.add_argument('--opts')
|
||||||
|
parser.add_argument('--include_dirs')
|
||||||
|
parser.add_argument('--lib_dir')
|
||||||
|
parser.add_argument('--libs')
|
||||||
|
parser.add_argument('--flags')
|
||||||
|
parser.add_argument('--outdir')
|
||||||
|
cv = parser.parse_args()
|
||||||
|
|
||||||
|
from filters import *
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
|
||||||
|
# populate the mex base template
|
||||||
|
substitute(cv, cv.outdir)
|
@ -139,10 +139,18 @@ def filename(fullpath):
|
|||||||
'''
|
'''
|
||||||
return os.path.splitext(os.path.basename(fullpath))[0]
|
return os.path.splitext(os.path.basename(fullpath))[0]
|
||||||
|
|
||||||
|
def split(text, delimiter=' '):
|
||||||
|
'''Split a text string into a list using the specified delimiter'''
|
||||||
|
return text.split(delimiter)
|
||||||
|
|
||||||
def csv(items, sep=', '):
|
def csv(items, sep=', '):
|
||||||
'''format a list with a separator (comma if not specified)'''
|
'''format a list with a separator (comma if not specified)'''
|
||||||
return sep.join(item for item in items)
|
return sep.join(item for item in items)
|
||||||
|
|
||||||
|
def cellarray(items, escape='\''):
|
||||||
|
'''format a list of items as a matlab cell array'''
|
||||||
|
return '{' + ', '.join(escape+item+escape for item in items) + '}'
|
||||||
|
|
||||||
def stripExtraSpaces(text):
|
def stripExtraSpaces(text):
|
||||||
'''Removes superfluous whitespace from a string, including the removal
|
'''Removes superfluous whitespace from a string, including the removal
|
||||||
of all leading and trailing whitespace'''
|
of all leading and trailing whitespace'''
|
||||||
|
@ -6,6 +6,8 @@ function buildInformation()
|
|||||||
% run into issues with the Toolbox, it is useful to submit this
|
% run into issues with the Toolbox, it is useful to submit this
|
||||||
% information alongside a bug report to the OpenCV team.
|
% information alongside a bug report to the OpenCV team.
|
||||||
%
|
%
|
||||||
|
% Copyright {{ time.strftime("%Y", time.localtime()) }} The OpenCV Foundation
|
||||||
|
%
|
||||||
info = {
|
info = {
|
||||||
' ------------------------------------------------------------------------'
|
' ------------------------------------------------------------------------'
|
||||||
' <strong>OpenCV Toolbox</strong>'
|
' <strong>OpenCV Toolbox</strong>'
|
||||||
|
46
modules/matlab/generator/templates/template_cvmex_base.m
Normal file
46
modules/matlab/generator/templates/template_cvmex_base.m
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
function mex(varargin)
|
||||||
|
%CV.MEX compile MEX-function with OpenCV linkages
|
||||||
|
%
|
||||||
|
% Usage:
|
||||||
|
% CV.MEX [options ...] file [file file ...]
|
||||||
|
%
|
||||||
|
% Description:
|
||||||
|
% CV.MEX compiles one or more C/C++ source files into a shared-library
|
||||||
|
% called a mex-file. This function is equivalent to the builtin MEX
|
||||||
|
% routine, with the notable exception that it automatically resolves
|
||||||
|
% OpenCV includes, and links in the OpenCV libraries where appropriate.
|
||||||
|
% It also forwards the flags used to build OpenCV, so architecture-
|
||||||
|
% specific optimizations can be used.
|
||||||
|
%
|
||||||
|
% CV.MEX is designed to be used in situations where the source(s) you
|
||||||
|
% are compiling contain OpenCV definitions. In such cases, it streamlines
|
||||||
|
% the finding and including of appropriate OpenCV libraries.
|
||||||
|
%
|
||||||
|
% See also: mex
|
||||||
|
%
|
||||||
|
% Copyright {{ time.strftime("%Y", time.localtime()) }} The OpenCV Foundation
|
||||||
|
%
|
||||||
|
|
||||||
|
% forward the OpenCV build flags (C++ only)
|
||||||
|
EXTRA_FLAGS = ['"CXXFLAGS="\$CXXFLAGS '...
|
||||||
|
'{{ cv.flags | trim | wordwrap(60, false, '\'...\n \'') }}""'];
|
||||||
|
|
||||||
|
% add the OpenCV include dirs
|
||||||
|
INCLUDE_DIRS = {{ cv.include_dirs | split | cellarray | wordwrap(60, false, '...\n ') }};
|
||||||
|
|
||||||
|
% add the lib dir (singular in both build tree and install tree)
|
||||||
|
LIB_DIR = '{{ cv.lib_dir }}';
|
||||||
|
|
||||||
|
% add the OpenCV libs. Only the used libs will actually be linked
|
||||||
|
LIBS = {{ cv.libs | split | cellarray | wordwrap(60, false, '...\n ') }};
|
||||||
|
|
||||||
|
% add the mex opts (usually at least -largeArrayDims)
|
||||||
|
OPTS = {{ cv.opts | split | cellarray | wordwrap(60, false, '...\n ') }};
|
||||||
|
|
||||||
|
% merge all of the default options (EXTRA_FLAGS, LIBS, etc) and the options
|
||||||
|
% and files passed by the user (varargin) into a single cell array
|
||||||
|
merged = [ {EXTRA_FLAGS}, INCLUDE_DIRS, {LIB_DIR}, LIBS, OPTS, varargin ];
|
||||||
|
|
||||||
|
% expand the merged argument list into the builtin mex utility
|
||||||
|
mex(merged{:});
|
||||||
|
end
|
@ -2,9 +2,19 @@
|
|||||||
#define OPENCV_MXARRAY_HPP_
|
#define OPENCV_MXARRAY_HPP_
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <cstdarg>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
#include <opencv2/core.hpp>
|
#include <opencv2/core.hpp>
|
||||||
|
#if __cplusplus > 201103
|
||||||
|
#include <unordered_set>
|
||||||
|
typedef std::unordered_set<std::string> StringSet;
|
||||||
|
#else
|
||||||
|
#include <set>
|
||||||
|
typedef std::set<std::string> StringSet;
|
||||||
|
#endif
|
||||||
#include "mex.h"
|
#include "mex.h"
|
||||||
#include "transpose.hpp"
|
#include "transpose.hpp"
|
||||||
|
|
||||||
@ -274,10 +284,10 @@ public:
|
|||||||
* Explicitly construct a scalar of given type. Since constructors cannot
|
* Explicitly construct a scalar of given type. Since constructors cannot
|
||||||
* be explicitly templated, this is a static factory method
|
* be explicitly templated, this is a static factory method
|
||||||
*/
|
*/
|
||||||
template <typename Scalar>
|
template <typename ScalarType>
|
||||||
static MxArray Scalar(Scalar value = 0) {
|
static MxArray Scalar(ScalarType value = 0) {
|
||||||
MxArray s(1, 1, 1, Matlab::Traits<Scalar>::ScalarType);
|
MxArray s(1, 1, 1, Matlab::Traits<ScalarType>::ScalarType);
|
||||||
s.real<Scalar>()[0] = value;
|
s.real<ScalarType>()[0] = value;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,8 +420,8 @@ public:
|
|||||||
|
|
||||||
std::string toString() const {
|
std::string toString() const {
|
||||||
conditionalError(isString(), "Attempted to convert non-string type to string");
|
conditionalError(isString(), "Attempted to convert non-string type to string");
|
||||||
std::string str(size()+1, '\0');
|
std::string str(size(), '\0');
|
||||||
mxGetString(ptr_, const_cast<char *>(str.data()), str.size());
|
mxGetString(ptr_, const_cast<char *>(str.data()), str.size()+1);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,6 +442,214 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*! @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()
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
class ArgumentParser {
|
||||||
|
private:
|
||||||
|
struct Variant;
|
||||||
|
typedef std::string String;
|
||||||
|
typedef std::vector<std::string> StringVector;
|
||||||
|
typedef std::vector<MxArray> MxArrayVector;
|
||||||
|
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 {
|
||||||
|
public:
|
||||||
|
Variant(const String& _name, size_t _nreq, size_t _nopt, const StringVector& _keys)
|
||||||
|
: name(_name), nreq(_nreq), nopt(_nopt), keys(_keys), using_named(false) {}
|
||||||
|
String name;
|
||||||
|
size_t nreq;
|
||||||
|
size_t nopt;
|
||||||
|
StringVector keys;
|
||||||
|
bool using_named;
|
||||||
|
/*! @brief return true if the named-argument is in the Variant */
|
||||||
|
bool count(const String& key) { return std::find(keys.begin(), keys.end(), key) != keys.end(); }
|
||||||
|
/*! @brief remove a key by index from the Variant */
|
||||||
|
void erase(const size_t idx) { keys.erase(keys.begin()+idx); }
|
||||||
|
/*! @brief remove a key by name from the Variant */
|
||||||
|
void erase(const String& key) { keys.erase(std::find(keys.begin(), keys.end(), key)); }
|
||||||
|
/*! @brief convert a Variant to a string representation */
|
||||||
|
String toString(const String& method_name=String("f")) const {
|
||||||
|
std::ostringstream s;
|
||||||
|
s << method_name << "(";
|
||||||
|
for (size_t n = 0; n < nreq; ++n) {
|
||||||
|
s << "src" << n+1; if (n != nreq-1) s << ", ";
|
||||||
|
}
|
||||||
|
if (nreq && nopt) s << ", ";
|
||||||
|
for (size_t n = 0; n < keys.size(); ++n) {
|
||||||
|
s << "'" << keys[n] << "', " << keys[n];
|
||||||
|
if (n != keys.size()-1) s << ", ";
|
||||||
|
}
|
||||||
|
s << ");";
|
||||||
|
return s.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MxArrayVector filled_;
|
||||||
|
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
|
||||||
|
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) {
|
||||||
|
// check if the input is a key
|
||||||
|
bool key = candidate->count(name);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FAILURE CASES
|
||||||
|
* 1. too many inputs, or
|
||||||
|
* 2. name is not a key and we're expecting a key
|
||||||
|
* 3. name is a key, and
|
||||||
|
* we're still expecting required arguments, or
|
||||||
|
* we're expecting an argument for a previous key
|
||||||
|
*/
|
||||||
|
if ((!candidate->nreq && !candidate->nopt) ||
|
||||||
|
(!key && !candidate->nreq && candidate->keys.size() == candidate->nopt && candidate->using_named) ||
|
||||||
|
(key && (candidate->nreq || candidate->keys.size() < candidate->nopt))) {
|
||||||
|
candidate = candidates.erase(candidate)--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALID CASES
|
||||||
|
// Still parsing required argments (input is not a key)
|
||||||
|
else if (!key && candidate->nreq) {
|
||||||
|
candidate->nreq--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parsing optional arguments and a named argument is encountered
|
||||||
|
else if (key && !candidate->nreq && candidate->nopt > 0 && candidate->keys.size() == candidate->nopt) {
|
||||||
|
candidate->erase(name);
|
||||||
|
candidate->using_named = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parsing input for a named argument
|
||||||
|
else if (!key && candidate->keys.size() < candidate->nopt) {
|
||||||
|
candidate->nopt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parsing un-named optional arguments
|
||||||
|
else if (!key && !candidate->nreq && candidate->nopt && candidate->keys.size() && !candidate->using_named) {
|
||||||
|
candidate->erase(0);
|
||||||
|
candidate->nopt--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if any candidates remain, check that they have been fully parsed
|
||||||
|
for (VariantVector::iterator candidate = candidates.begin(); candidate < candidates.end(); ++candidate) {
|
||||||
|
if (candidate->nreq || candidate->keys.size() < candidate->nopt) {
|
||||||
|
candidate = candidates.erase(candidate)--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if there is not a unique candidate, throw an error
|
||||||
|
String variant_string;
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @brief template specialization for inheriting types
|
* @brief template specialization for inheriting types
|
||||||
*
|
*
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Copyright 2013 The OpenCV Foundation
|
* Copyright 2013 The OpenCV Foundation
|
||||||
*/
|
*/
|
||||||
#include <exception>
|
#include <exception>
|
||||||
//#include <opencv2/core.hpp>
|
#include <opencv2/core.hpp>
|
||||||
#include "mex.h"
|
#include "mex.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -24,9 +24,9 @@ void mexFunction(int nlhs, mxArray* plhs[],
|
|||||||
// call the opencv function
|
// call the opencv function
|
||||||
// [out =] namespace.fun(src1, ..., srcn, dst1, ..., dstn, opt1, ..., optn);
|
// [out =] namespace.fun(src1, ..., srcn, dst1, ..., dstn, opt1, ..., optn);
|
||||||
try {
|
try {
|
||||||
// throw cv::exception;
|
throw cv::Exception(-1, "OpenCV exception thrown", __func__, __FILE__, __LINE__);
|
||||||
//} catch(cv::exception& e) {
|
} catch(cv::Exception& e) {
|
||||||
// mexErrMsgTxt(e.what());
|
mexErrMsgTxt(e.what());
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
mexErrMsgTxt("Incorrect exception caught!");
|
mexErrMsgTxt("Incorrect exception caught!");
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user