Commit baa946c3 authored by hbristow's avatar hbristow

Split MxArray out of bridge into standalone file. Does not depend on bridge.…

Split MxArray out of bridge into standalone file. Does not depend on bridge. Working to make it API complete with respect to mxArray, but with better calling syntax
parent 504558c0
...@@ -58,9 +58,9 @@ class MatlabWrapperGenerator(object): ...@@ -58,9 +58,9 @@ class MatlabWrapperGenerator(object):
# populate templates # populate templates
for namespace in parse_tree.namespaces: for namespace in parse_tree.namespaces:
# functions # functions
for function in namespace.functions: for method in namespace.methods:
populated = tfunction.render(fun=function, time=time, includes=namespace.name) populated = tfunction.render(fun=method, time=time, includes=namespace.name)
with open(output_source_dir+'/'+function.name+'.cpp', 'wb') as f: with open(output_source_dir+'/'+method.name+'.cpp', 'wb') as f:
f.write(populated) f.write(populated)
# classes # classes
for clss in namespace.classes: for clss in namespace.classes:
......
...@@ -13,7 +13,7 @@ class ParseTree(object): ...@@ -13,7 +13,7 @@ class ParseTree(object):
babel = Translator() babel = Translator()
for name, definitions in namespaces.items(): for name, definitions in namespaces.items():
class_tree = {} class_tree = {}
functions = [] methods = []
constants = [] constants = []
for defn in definitions: for defn in definitions:
obj = babel.translate(defn) obj = babel.translate(defn)
...@@ -21,13 +21,13 @@ class ParseTree(object): ...@@ -21,13 +21,13 @@ class ParseTree(object):
continue continue
if type(obj) is Class or obj.clss: if type(obj) is Class or obj.clss:
self.insertIntoClassTree(obj, class_tree) self.insertIntoClassTree(obj, class_tree)
elif type(obj) is Function: elif type(obj) is Method:
functions.append(obj) methods.append(obj)
elif type(obj) is Constant: elif type(obj) is Constant:
constants.append(obj) constants.append(obj)
else: else:
raise TypeError('Unexpected object type: '+str(type(obj))) raise TypeError('Unexpected object type: '+str(type(obj)))
self.namespaces.append(Namespace(name, constants, class_tree.values(), functions)) self.namespaces.append(Namespace(name, constants, class_tree.values(), methods))
def insertIntoClassTree(self, obj, class_tree): def insertIntoClassTree(self, obj, class_tree):
cname = obj.name if type(obj) is Class else obj.clss cname = obj.name if type(obj) is Class else obj.clss
...@@ -38,8 +38,8 @@ class ParseTree(object): ...@@ -38,8 +38,8 @@ class ParseTree(object):
class_tree[cname] = Class(cname) class_tree[cname] = Class(cname)
# insert the definition into the class # insert the definition into the class
val = class_tree[cname] val = class_tree[cname]
if type(obj) is Function: if type(obj) is Method:
val.functions.append(obj) val.methods.append(obj)
elif type(obj) is Constant: elif type(obj) is Constant:
val.constants.append(obj) val.constants.append(obj)
else: else:
...@@ -63,7 +63,7 @@ class Translator(object): ...@@ -63,7 +63,7 @@ class Translator(object):
# --- function --- # --- function ---
# functions either need to have input arguments, or not uppercase names # functions either need to have input arguments, or not uppercase names
elif defn[3] or not self.translateName(defn[0]).split('_')[0].isupper(): elif defn[3] or not self.translateName(defn[0]).split('_')[0].isupper():
return self.translateFunction(defn) return self.translateMethod(defn)
# --- constant --- # --- constant ---
else: else:
return self.translateConstant(defn) return self.translateConstant(defn)
...@@ -71,7 +71,7 @@ class Translator(object): ...@@ -71,7 +71,7 @@ class Translator(object):
def translateClass(self, defn): def translateClass(self, defn):
return Class() return Class()
def translateFunction(self, defn, class_tree=None): def translateMethod(self, defn, class_tree=None):
name = self.translateName(defn[0]) name = self.translateName(defn[0])
clss = self.translateClassName(defn[0]) clss = self.translateClassName(defn[0])
rtp = defn[1] rtp = defn[1]
...@@ -83,7 +83,7 @@ class Translator(object): ...@@ -83,7 +83,7 @@ class Translator(object):
if arg: if arg:
a = self.translateArgument(arg) a = self.translateArgument(arg)
opt.append(a) if a.default else req.append(a) opt.append(a) if a.default else req.append(a)
return Function(name, clss, static, '', rtp, False, req, opt) return Method(name, clss, static, '', rtp, False, req, opt)
def translateConstant(self, defn): def translateConstant(self, defn):
const = True if 'const' in defn[0] else False const = True if 'const' in defn[0] else False
...@@ -116,34 +116,35 @@ class Translator(object): ...@@ -116,34 +116,35 @@ class Translator(object):
class Namespace(object): class Namespace(object):
def __init__(self, name='', constants=None, classes=None, functions=None): def __init__(self, name='', constants=None, classes=None, methods=None):
self.name = name self.name = name
self.constants = constants if constants else [] self.constants = constants if constants else []
self.classes = classes if classes else [] self.classes = classes if classes else []
self.functions = functions if functions else [] self.methods = methods if methods else []
def __str__(self): def __str__(self):
return 'namespace '+self.name+' {\n\n'+\ return 'namespace '+self.name+' {\n\n'+\
(join((c.__str__() for c in self.constants), '\n')+'\n\n' if self.constants else '')+\ (join((c.__str__() for c in self.constants), '\n')+'\n\n' if self.constants else '')+\
(join((f.__str__() for f in self.functions), '\n')+'\n\n' if self.functions else '')+\ (join((f.__str__() for f in self.methods), '\n')+'\n\n' if self.methods else '')+\
(join((o.__str__() for o in self.classes), '\n\n') if self.classes else '')+'\n};' (join((o.__str__() for o in self.classes), '\n\n') if self.classes else '')+'\n};'
class Class(object): class Class(object):
def __init__(self, name='', namespace='', constants=None, functions=None): def __init__(self, name='', namespace='', constants=None, methods=None):
self.name = name self.name = name
self.namespace = namespace self.namespace = namespace
self.constants = constants if constants else [] self.constants = constants if constants else []
self.functions = functions if functions else [] self.methods = methods if methods else []
def __str__(self): def __str__(self):
return 'class '+self.name+' {\n\t'+\ return 'class '+self.name+' {\n\t'+\
(join((c.__str__() for c in self.constants), '\n\t')+'\n\n\t' if self.constants else '')+\ (join((c.__str__() for c in self.constants), '\n\t')+'\n\n\t' if self.constants else '')+\
(join((f.__str__() for f in self.functions), '\n\t') if self.functions else '')+'\n};' (join((f.__str__() for f in self.methods), '\n\t') if self.methods else '')+'\n};'
class Function(object): class Method(object):
def __init__(self, name='', clss='', static=False, namespace='', rtp='', const=False, req=None, opt=None): def __init__(self, name='', clss='', static=False, namespace='', rtp='', const=False, req=None, opt=None):
self.name = name self.name = name
self.clss = clss self.clss = clss
self.constructor = True if name == clss else False
self.static = static self.static = static
self.const = const self.const = const
self.namespace = namespace self.namespace = namespace
......
/* /*
* compose * compose
* compose a function call * compose a function call
* This macro takes as input a Function object and composes * This macro takes as input a Method object and composes
* a function call by inspecting the types and argument names * a function call by inspecting the types and argument names
*/ */
/
{% macro compose(fun) %} {% macro compose(fun) %}
{# ----------- Return type ------------- #} {# ----------- Return type ------------- #}
{%- if not fun.rtp|void -%} retval = {% endif -%} {%- if not fun.rtp|void and not fun.constructor -%} retval = {% endif -%}
{%- if fun.clss -%}inst.{%- else -%} cv:: {%- endif -%} {%- if fun.constructor -%}{{fun.clss}} obj = {% endif -%}
{%- if fun.clss and not fun.constructor -%}inst.{%- else -%} cv:: {%- endif -%}
{{fun.name}}( {{fun.name}}(
{#- ----------- Required ------------- -#} {#- ----------- Required ------------- -#}
{%- for arg in fun.req -%} {%- for arg in fun.req -%}
...@@ -26,10 +26,39 @@ ...@@ -26,10 +26,39 @@
); );
{%- endmacro %} {%- endmacro %}
// create a full function invocation
{%- macro generate(fun) -%}
{% if fun|ninputs or fun|noutputs %} /*
* composeWithExceptionHandler
* compose a function call wrapped in exception traps
* This macro takes an input a Method object and composes a function
* call through the compose() macro, then wraps the return in traps
* for cv::Exceptions, std::exceptions, and all generic exceptions
* and returns a useful error message to the Matlab interpreter
*/
{%- macro composeWithExceptionHandler(fun) -%}
// call the opencv function
// [out =] namespace.fun(src1, ..., srcn, dst1, ..., dstn, opt1, ..., optn);
try {
{{ compose(fun) }}
} catch(cv::Exception& e) {
error(std::string("cv::exception caught: ").append(e.what()).c_str());
} catch(std::exception& e) {
error(std::string("std::exception caught: ").append(e.what()).c_str());
} catch(...) {
error("Uncaught exception occurred in {{fun.name}}");
}
{%- endmacro %}
/*
* handleInputs
* unpack input arguments from the Bridge
* Given an input Bridge object, this unpacks the object from the Bridge and
* casts them into the correct type
*/
{%- macro handleInputs(fun) %}
{% if fun|ninputs or (fun|noutputs and not fun.constructor) %}
// unpack the arguments // unpack the arguments
{# ----------- Inputs ------------- #} {# ----------- Inputs ------------- #}
{% for arg in fun.req|inputs %} {% for arg in fun.req|inputs %}
...@@ -45,26 +74,24 @@ ...@@ -45,26 +74,24 @@
{% for opt in fun.opt|only|outputs %} {% for opt in fun.opt|only|outputs %}
{{opt.tp}} {{opt.name}}; {{opt.tp}} {{opt.name}};
{% endfor %} {% endfor %}
{% if not fun.rtp|void %} {% if not fun.rtp|void and not fun.constructor %}
{{fun.rtp}} retval; {{fun.rtp}} retval;
{% endif %} {% endif %}
{% endif %} {% endif %}
// call the opencv function {%- endmacro %}
// [out =] namespace.fun(src1, ..., srcn, dst1, ..., dstn, opt1, ..., optn);
try { /*
{{ compose(fun) }} * handleOutputs
} catch(cv::Exception& e) { * pack outputs into the bridge
mexErrMsgTxt(std::string("cv::exception caught: ").append(e.what()).c_str()); * Given a set of outputs, this methods assigns them into the bridge for
} catch(std::exception& e) { * return to the calling method
mexErrMsgTxt(std::string("std::exception caught: ").append(e.what()).c_str()); */
} catch(...) { {%- macro handleOutputs(fun) %}
mexErrMsgTxt("Uncaught exception occurred in {{fun.name}}");
}
{% if fun|noutputs %} {% if fun|noutputs %}
// assign the outputs into the bridge // assign the outputs into the bridge
{% if not fun.rtp|void %} {% if not fun.rtp|void and not fun.constructor %}
outputs[0] = retval; outputs[0] = retval;
{% endif %} {% endif %}
{% for arg in fun.req|outputs %} {% for arg in fun.req|outputs %}
...@@ -74,5 +101,4 @@ ...@@ -74,5 +101,4 @@
outputs[{{loop.index0 + fun.rtp|void|not + fun.req|outputs|length}}] = {{opt.name}}; outputs[{{loop.index0 + fun.rtp|void|not + fun.req|outputs|length}}] = {{opt.name}};
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{%- endmacro %}
{% endmacro %}
...@@ -9,30 +9,41 @@ ...@@ -9,30 +9,41 @@
* Copyright {{time.strftime("%Y", time.localtime())}} The OpenCV Foundation * Copyright {{time.strftime("%Y", time.localtime())}} The OpenCV Foundation
*/ */
#include "mex.h" #include "mex.h"
#include "map.hpp"
#include "bridge.hpp" #include "bridge.hpp"
#include <vector> #include <vector>
//TODO: Standard C++ does not have an unordered_map (only C++11 and Boost)
#include <unordered_map>
#include <string> #include <string>
#include <opencv2/core.hpp> #include <opencv2/core.hpp>
using namespace cv; using namespace cv;
namespace { namespace {
typedef std::unordered_map Map;
typedef std::vector<Bridge> (*)({{clss.name}}&, const std::vector<Bridge>&) MethodSignature; typedef std::vector<Bridge> (*)({{clss.name}}&, const std::vector<Bridge>&) MethodSignature;
{% for function in clss.functions %} {% for function in clss.methods %}
{% if function.constructor %}
// wrapper for {{function.name}}() constructor
{{ function.clss }} {{function.name}}(const std::vector<Bridge>& inputs) {
{{ functional.handleInputs(function) }}
{{ functional.compose(function) }}
return obj;
}
{% else %}
// wrapper for {{function.name}}() method // wrapper for {{function.name}}() method
std::vector<Bridge> {{function.name}}({{clss.name}}& inst, const std::vector<Bridge>& args) { std::vector<Bridge> {{function.name}}({{clss.name}}& inst, const std::vector<Bridge>& inputs) {
{{ functional.generate(function) }} std::vector<Bridge> outputs{% if function|noutputs %}({{function|noutputs}}){% endif %};
{{ functional.handleInputs(function) }}
{{ functional.composeWithExceptionHandler(function) }}
{{ functional.handleOutputs(function) }}
return outputs;
} }
{% endif %}
{% endfor %} {% endfor %}
map<std::string, MethodSignature> createMethodMap() { Map<std::string, MethodSignature> createMethodMap() {
Map<std::string, MethodSignature> m; Map<std::string, MethodSignature> m;
{% for function in clss.functions -%} {% for function in clss.methods %}
m["{{function.name}}"] = &{{function.name}}; m["{{function.name}}"] = &{{function.name}};
{% endfor %} {% endfor %}
...@@ -82,4 +93,4 @@ void mexFunction(int nlhs, mxArray* plhs[], ...@@ -82,4 +93,4 @@ void mexFunction(int nlhs, mxArray* plhs[],
} }
}; // end namespace } // end namespace
...@@ -44,9 +44,11 @@ void mexFunction(int nlhs, mxArray*{% if fun|noutputs %} plhs[]{% else %}*{% end ...@@ -44,9 +44,11 @@ void mexFunction(int nlhs, mxArray*{% if fun|noutputs %} plhs[]{% else %}*{% end
{% endif %} {% endif %}
{% endif %} {% endif %}
{{ functional.generate(fun) }} {{ functional.handleInputs(fun) }}
{{ functional.composeWithExceptionHandler(fun) }}
{{ functional.handleOutputs(fun) }}
{%- if fun|noutputs %} {% if fun|noutputs %}
// push the outputs back to matlab // push the outputs back to matlab
for (size_t n = 0; n < static_cast<size_t>(nlhs); ++n) { for (size_t n = 0; n < static_cast<size_t>(nlhs); ++n) {
plhs[n] = outputs[n].toMxArray().releaseOwnership(); plhs[n] = outputs[n].toMxArray().releaseOwnership();
......
This diff is collapsed.
This diff is collapsed.
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