diff --git a/wrap/Class.cpp b/wrap/Class.cpp index 8e8f42045..a33f89f35 100644 --- a/wrap/Class.cpp +++ b/wrap/Class.cpp @@ -70,7 +70,7 @@ void Class::matlab_proxy(const string& classFile, const string& wrapperName, Fil functionNames.push_back(wrapFunctionName); } proxyFile.oss << " else\n"; - proxyFile.oss << " error('" << matlabName << " constructor failed');" << endl; + proxyFile.oss << " error('Arguments do not match any overload of " << matlabName << " constructor');" << endl; proxyFile.oss << " end\n"; proxyFile.oss << " end\n\n"; @@ -88,14 +88,23 @@ void Class::matlab_proxy(const string& classFile, const string& wrapperName, Fil proxyFile.oss << " function disp(obj), obj.display; end\n\n"; // Methods - BOOST_FOREACH(Method m, methods) { - const int id = functionNames.size(); - m.proxy_fragment(proxyFile, wrapperName, id); + BOOST_FOREACH(const Methods::value_type& name_m, methods) { + const Method& m = name_m.second; + m.proxy_wrapper_fragments(proxyFile, wrapperFile, cppName, matlabName, wrapperName, using_namespaces, functionNames); + proxyFile.oss << "\n"; + wrapperFile.oss << "\n"; + } + + proxyFile.oss << " end\n"; + proxyFile.oss << "\n"; + proxyFile.oss << " methods(Static = true)\n"; + + // Static methods + BOOST_FOREACH(const StaticMethods::value_type& name_m, static_methods) { + const StaticMethod& m = name_m.second; + m.proxy_wrapper_fragments(proxyFile, wrapperFile, cppName, matlabName, wrapperName, using_namespaces, functionNames); proxyFile.oss << "\n"; - const string wrapFunctionName = m.wrapper_fragment(wrapperFile, - cppName, matlabName, id, using_namespaces); wrapperFile.oss << "\n"; - functionNames.push_back(wrapFunctionName); } proxyFile.oss << " end" << endl; @@ -105,18 +114,6 @@ void Class::matlab_proxy(const string& classFile, const string& wrapperName, Fil proxyFile.emit(true); } -/* ************************************************************************* */ -void Class::matlab_static_methods(const string& toolboxPath, const string& wrapperName, - FileWriter& wrapperFile, vector& functionNames) const { - string matlabName = qualifiedName(), cppName = qualifiedName("::"); - BOOST_FOREACH(const StaticMethod& m, static_methods) { - const int id = functionNames.size(); - m.proxy_fragment(toolboxPath, matlabName, wrapperName, id); - const string wrapFunction = m.wrapper_fragment(wrapperFile, matlabName, cppName, id, using_namespaces); - functionNames.push_back(wrapFunction); - } -} - /* ************************************************************************* */ string Class::qualifiedName(const string& delim) const { string result; diff --git a/wrap/Class.h b/wrap/Class.h index 736e0a370..f1a745d94 100644 --- a/wrap/Class.h +++ b/wrap/Class.h @@ -19,7 +19,7 @@ #pragma once #include -#include +#include #include "Constructor.h" #include "Deconstructor.h" @@ -30,13 +30,16 @@ namespace wrap { /// Class has name, constructors, methods struct Class { + typedef std::map Methods; + typedef std::map StaticMethods; + /// Constructor creates an empty class Class(bool verbose=true) : verbose_(verbose) {} // Then the instance variables are set directly by the Module constructor std::string name; ///< Class name - std::vector methods; ///< Class methods - std::vector static_methods; ///< Static methods + Methods methods; ///< Class methods + StaticMethods static_methods; ///< Static methods std::vector namespaces; ///< Stack of namespaces std::vector using_namespaces;///< default namespaces std::vector includes; ///< header include overrides @@ -47,8 +50,6 @@ struct Class { // And finally MATLAB code is emitted, methods below called by Module::matlab_code void matlab_proxy(const std::string& classFile, const std::string& wrapperName, FileWriter& wrapperFile, std::vector& functionNames) const; ///< emit proxy class - void matlab_static_methods(const std::string& toolboxPath, const std::string& wrapperName, - FileWriter& wrapperFile, std::vector& functionNames) const; ///< emit static method wrappers std::string qualifiedName(const std::string& delim = "") const; ///< creates a namespace-qualified name, optional delimiter private: diff --git a/wrap/Method.cpp b/wrap/Method.cpp index bcca86d40..88176f675 100644 --- a/wrap/Method.cpp +++ b/wrap/Method.cpp @@ -27,24 +27,78 @@ using namespace std; using namespace wrap; /* ************************************************************************* */ -void Method::proxy_fragment(FileWriter& file, const std::string& wrapperName, const int id) const { +void Method::addOverload(bool verbose, bool is_const, const std::string& name, + const ArgumentList& args, const ReturnValue& retVal) { + this->verbose_ = verbose; + this->is_const_ = is_const; + this->name = name; + this->argLists.push_back(args); + this->returnVals.push_back(retVal); +} - string output; - if(returnVal.isPair) - output = "[ r1 r2 ] = "; - else if(returnVal.category1 == ReturnValue::VOID) - output = ""; - else - output = "r = "; - file.oss << " function " << output << name << "(varargin)\n"; - file.oss << " " << output << wrapperName << "(" << id << ", varargin{:});\n"; - file.oss << " end\n"; +void Method::proxy_wrapper_fragments(FileWriter& proxyFile, FileWriter& wrapperFile, + const string& cppClassName, + const string& matlabClassName, + const string& wrapperName, + const vector& using_namespaces, + vector& functionNames) const { + + proxyFile.oss << " function varargout = " << name << "(self, varargin)\n"; + + for(size_t overload = 0; overload < argLists.size(); ++overload) { + const ArgumentList& args = argLists[overload]; + const ReturnValue& returnVal = returnVals[overload]; + size_t nrArgs = args.size(); + + const int id = functionNames.size(); + + // Output proxy matlab code + + // check for number of arguments... + proxyFile.oss << " " << (overload==0?"":"else") << "if length(varargin) == " << nrArgs; + if (nrArgs>0) proxyFile.oss << " && "; + // ...and their types + bool first = true; + for(size_t i=0;i& using_namespaces) const { @@ -52,6 +106,9 @@ string Method::wrapper_fragment(FileWriter& file, const string wrapFunctionName = matlabClassName + "_" + name + "_" + boost::lexical_cast(id); + const ArgumentList& args = argLists[overload]; + const ReturnValue& returnVal = returnVals[overload]; + // call file.oss << "void " << wrapFunctionName << "(int nargout, mxArray *out[], int nargin, const mxArray *in[])\n"; // start diff --git a/wrap/Method.h b/wrap/Method.h index 60b0c1282..204e3002d 100644 --- a/wrap/Method.h +++ b/wrap/Method.h @@ -36,16 +36,27 @@ struct Method { bool verbose_; bool is_const_; std::string name; - ArgumentList args; - ReturnValue returnVal; + std::vector argLists; + std::vector returnVals; + + // The first time this function is called, it initializes the class members + // with those in rhs, but in subsequent calls it adds additional argument + // lists as function overloads. + void addOverload(bool verbose, bool is_const, const std::string& name, + const ArgumentList& args, const ReturnValue& retVal); // MATLAB code generation // classPath is class directory, e.g., ../matlab/@Point2 + void proxy_wrapper_fragments(FileWriter& proxyFile, FileWriter& wrapperFile, + const std::string& cppClassName, const std::string& matlabClassName, + const std::string& wrapperName, const std::vector& using_namespaces, + std::vector& functionNames) const; - void proxy_fragment(FileWriter& file, const std::string& wrapperName, const int id) const; +private: std::string wrapper_fragment(FileWriter& file, const std::string& cppClassName, const std::string& matlabClassname, + int overload, int id, const std::vector& using_namespaces) const; ///< cpp wrapper }; diff --git a/wrap/Module.cpp b/wrap/Module.cpp index 400658044..0c351a4f9 100644 --- a/wrap/Module.cpp +++ b/wrap/Module.cpp @@ -24,6 +24,8 @@ //#define BOOST_SPIRIT_DEBUG #include #include +#include +#include #include #include #include @@ -34,6 +36,7 @@ using namespace std; using namespace wrap; using namespace BOOST_SPIRIT_CLASSIC_NS; +namespace bl = boost::lambda; namespace fs = boost::filesystem; typedef rule Rule; @@ -51,13 +54,15 @@ Module::Module(const string& interfacePath, { // these variables will be imperatively updated to gradually build [cls] // The one with postfix 0 are used to reset the variables after parse. + string methodName, methodName0; + bool isConst, isConst0 = false; ReturnValue retVal0, retVal; Argument arg0, arg; ArgumentList args0, args; vector arg_dup; ///keep track of duplicates Constructor constructor0(enable_verbose), constructor(enable_verbose); Deconstructor deconstructor0(enable_verbose), deconstructor(enable_verbose); - Method method0(enable_verbose), method(enable_verbose); + //Method method0(enable_verbose), method(enable_verbose); StaticMethod static_method0(enable_verbose), static_method(enable_verbose); Class cls0(enable_verbose),cls(enable_verbose); vector namespaces, /// current namespace tag @@ -155,27 +160,35 @@ Module::Module(const string& interfacePath, Rule methodName_p = lexeme_d[lower_p >> *(alnum_p | '_')]; Rule method_p = - (returnType_p >> methodName_p[assign_a(method.name)] >> + (returnType_p >> methodName_p[assign_a(methodName)] >> '(' >> argumentList_p >> ')' >> - !str_p("const")[assign_a(method.is_const_,true)] >> ';' >> *comments_p) - [assign_a(method.args,args)] + !str_p("const")[assign_a(isConst,true)] >> ';' >> *comments_p) + [bl::bind(&Method::addOverload, + bl::var(cls.methods)[bl::var(methodName)], + verbose, + bl::var(isConst), + bl::var(methodName), + bl::var(args), + bl::var(retVal))] + [assign_a(isConst,isConst0)] + [assign_a(methodName,methodName0)] [assign_a(args,args0)] - [assign_a(method.returnVal,retVal)] - [assign_a(retVal,retVal0)] - [push_back_a(cls.methods, method)] - [assign_a(method,method0)]; + [assign_a(retVal,retVal0)]; Rule staticMethodName_p = lexeme_d[(upper_p | lower_p) >> *(alnum_p | '_')]; Rule static_method_p = - (str_p("static") >> returnType_p >> staticMethodName_p[assign_a(static_method.name)] >> + (str_p("static") >> returnType_p >> staticMethodName_p[assign_a(methodName)] >> '(' >> argumentList_p >> ')' >> ';' >> *comments_p) - [assign_a(static_method.args,args)] + [bl::bind(&StaticMethod::addOverload, + bl::var(cls.static_methods)[bl::var(methodName)], + verbose, + bl::var(methodName), + bl::var(args), + bl::var(retVal))] + [assign_a(methodName,methodName0)] [assign_a(args,args0)] - [assign_a(static_method.returnVal,retVal)] - [assign_a(retVal,retVal0)] - [push_back_a(cls.static_methods, static_method)] - [assign_a(static_method,static_method0)]; + [assign_a(retVal,retVal0)]; Rule functions_p = constructor_p | method_p | static_method_p; @@ -262,26 +275,33 @@ Module::Module(const string& interfacePath, /* ************************************************************************* */ template -void verifyArguments(const vector& validArgs, const vector& vt) { - BOOST_FOREACH(const T& t, vt) { - BOOST_FOREACH(Argument arg, t.args) { - string fullType = arg.qualifiedType("::"); - if(find(validArgs.begin(), validArgs.end(), fullType) - == validArgs.end()) - throw DependencyMissing(fullType, t.name); +void verifyArguments(const vector& validArgs, const map& vt) { + typedef map::value_type Name_Method; + BOOST_FOREACH(const Name_Method& name_method, vt) { + const T& t = name_method.second; + BOOST_FOREACH(const ArgumentList& argList, t.argLists) { + BOOST_FOREACH(Argument arg, argList) { + string fullType = arg.qualifiedType("::"); + if(find(validArgs.begin(), validArgs.end(), fullType) + == validArgs.end()) + throw DependencyMissing(fullType, t.name); + } } } } /* ************************************************************************* */ template -void verifyReturnTypes(const vector& validtypes, const vector& vt) { - BOOST_FOREACH(const T& t, vt) { - const ReturnValue& retval = t.returnVal; - if (find(validtypes.begin(), validtypes.end(), retval.qualifiedType1("::")) == validtypes.end()) - throw DependencyMissing(retval.qualifiedType1("::"), t.name); - if (retval.isPair && find(validtypes.begin(), validtypes.end(), retval.qualifiedType2("::")) == validtypes.end()) - throw DependencyMissing(retval.qualifiedType2("::"), t.name); +void verifyReturnTypes(const vector& validtypes, const map& vt) { + typedef map::value_type Name_Method; + BOOST_FOREACH(const Name_Method& name_method, vt) { + const T& t = name_method.second; + BOOST_FOREACH(const ReturnValue& retval, t.returnVals) { + if (find(validtypes.begin(), validtypes.end(), retval.qualifiedType1("::")) == validtypes.end()) + throw DependencyMissing(retval.qualifiedType1("::"), t.name); + if (retval.isPair && find(validtypes.begin(), validtypes.end(), retval.qualifiedType2("::")) == validtypes.end()) + throw DependencyMissing(retval.qualifiedType2("::"), t.name); + } } } @@ -350,7 +370,7 @@ void Module::matlab_code(const string& toolboxPath, const string& headerPath) co // generate proxy classes and wrappers BOOST_FOREACH(Class cls, classes) { - // create proxy class + // create proxy class and wrapper code string classFile = toolboxPath + "/" + cls.qualifiedName() + ".m"; cls.matlab_proxy(classFile, wrapperName, wrapperFile, functionNames); @@ -362,9 +382,6 @@ void Module::matlab_code(const string& toolboxPath, const string& headerPath) co // verify function return types verifyReturnTypes(validTypes, cls.static_methods); verifyReturnTypes(validTypes, cls.methods); - - // create constructor and method wrappers - cls.matlab_static_methods(toolboxPath, wrapperName, wrapperFile, functionNames); } // finish wrapper file diff --git a/wrap/StaticMethod.cpp b/wrap/StaticMethod.cpp index 8cdf0e597..dd0c606b5 100644 --- a/wrap/StaticMethod.cpp +++ b/wrap/StaticMethod.cpp @@ -27,34 +27,91 @@ using namespace std; using namespace wrap; + /* ************************************************************************* */ -void StaticMethod::proxy_fragment(const string& toolboxPath, const string& matlabClassName, const std::string& wrapperName, const int id) const { +void StaticMethod::addOverload(bool verbose, const std::string& name, + const ArgumentList& args, const ReturnValue& retVal) { + this->verbose = verbose; + this->name = name; + this->argLists.push_back(args); + this->returnVals.push_back(retVal); +} - const string full_name = matlabClassName + "_" + name; - FileWriter file(toolboxPath + "/" + full_name + ".m", verbose, "%"); +void StaticMethod::proxy_wrapper_fragments(FileWriter& proxyFile, FileWriter& wrapperFile, + const string& cppClassName, + const string& matlabClassName, + const string& wrapperName, + const vector& using_namespaces, + vector& functionNames) const { - string output; - if(returnVal.isPair) - output = "[ r1 r2 ] = "; - else if(returnVal.category1 == ReturnValue::VOID) - output = ""; - else - output = "r = "; - file.oss << "function " << output << full_name << "(varargin)\n"; - file.oss << " " << output << wrapperName << "(" << id << ", varargin{:});\n"; - file.oss << "end\n"; + string upperName = name; upperName[0] = std::toupper(upperName[0], std::locale()); - file.emit(true); + proxyFile.oss << " function varargout = " << upperName << "(varargin)\n"; + + for(size_t overload = 0; overload < argLists.size(); ++overload) { + const ArgumentList& args = argLists[overload]; + const ReturnValue& returnVal = returnVals[overload]; + size_t nrArgs = args.size(); + + const int id = functionNames.size(); + + // Output proxy matlab code + + // check for number of arguments... + proxyFile.oss << " " << (overload==0?"":"else") << "if length(varargin) == " << nrArgs; + if (nrArgs>0) proxyFile.oss << " && "; + // ...and their types + bool first = true; + for(size_t i=0;i& using_namespaces) const { - - const string full_name = matlabClassName + "_" + name; +string StaticMethod::wrapper_fragment(FileWriter& file, + const string& cppClassName, + const string& matlabClassName, + int overload, + int id, + const vector& using_namespaces) const { + + // generate code + const string wrapFunctionName = matlabClassName + "_" + name + "_" + boost::lexical_cast(id); + const ArgumentList& args = argLists[overload]; + const ReturnValue& returnVal = returnVals[overload]; + // call file.oss << "void " << wrapFunctionName << "(int nargout, mxArray *out[], int nargin, const mxArray *in[])\n"; // start @@ -76,7 +133,7 @@ string StaticMethod::wrapper_fragment(FileWriter& file, // check arguments // NOTE: for static functions, there is no object passed - file.oss << " checkArguments(\"" << full_name << "\",nargout,nargin," << args.size() << ");\n"; + file.oss << " checkArguments(\"" << matlabClassName << "." << name << "\",nargout,nargin," << args.size() << ");\n"; // unwrap arguments, see Argument.cpp args.matlab_unwrap(file,0); // We start at 0 because there is no self object diff --git a/wrap/StaticMethod.h b/wrap/StaticMethod.h index 3baa58585..5bc599ea5 100644 --- a/wrap/StaticMethod.h +++ b/wrap/StaticMethod.h @@ -36,21 +36,29 @@ struct StaticMethod { // Then the instance variables are set directly by the Module constructor bool verbose; std::string name; - ArgumentList args; - ReturnValue returnVal; + std::vector argLists; + std::vector returnVals; + + // The first time this function is called, it initializes the class members + // with those in rhs, but in subsequent calls it adds additional argument + // lists as function overloads. + void addOverload(bool verbose, const std::string& name, + const ArgumentList& args, const ReturnValue& retVal); // MATLAB code generation - // toolboxPath is the core toolbox directory, e.g., ../matlab - // NOTE: static functions are not inside the class, and - // are created with [ClassName]_[FunctionName]() format + // classPath is class directory, e.g., ../matlab/@Point2 + void proxy_wrapper_fragments(FileWriter& proxyFile, FileWriter& wrapperFile, + const std::string& cppClassName, const std::string& matlabClassName, + const std::string& wrapperName, const std::vector& using_namespaces, + std::vector& functionNames) const; - void proxy_fragment(const std::string& toolboxPath, const std::string& matlabClassName, - const std::string& wrapperName, const int id) const; ///< m-file +private: std::string wrapper_fragment(FileWriter& file, - const std::string& matlabClassName, - const std::string& cppClassName, + const std::string& cppClassName, + const std::string& matlabClassname, + int overload, int id, - const std::vector& using_namespaces) const; ///< cpp wrapper + const std::vector& using_namespaces) const; ///< cpp wrapper }; } // \namespace wrap