From 7a4748d3dcb008889dffbdbb027aafb2db250a23 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 14 Nov 2014 16:43:53 +0100 Subject: [PATCH 1/4] Simplified method/function hierarchy drastically, and renamed bottom addOverload to initializeOrCheck to reflect what it does. Also, gratuitous re-ordering of addOverload arguments. --- wrap/Argument.h | 8 -- wrap/Class.cpp | 8 +- wrap/Constructor.h | 18 ++-- wrap/FullyOverloadedFunction.h | 133 +++++++++++++++++++++++ wrap/Function.cpp | 43 +++----- wrap/Function.h | 186 ++------------------------------- wrap/GlobalFunction.cpp | 23 ++-- wrap/GlobalFunction.h | 22 ++-- wrap/Method.cpp | 22 ++-- wrap/Method.h | 18 +--- wrap/Module.cpp | 9 +- wrap/OverloadedFunction.h | 126 ++++++++++++++++++++++ wrap/StaticMethod.cpp | 8 -- wrap/StaticMethod.h | 16 +-- wrap/tests/testMethod.cpp | 9 +- 15 files changed, 341 insertions(+), 308 deletions(-) create mode 100644 wrap/FullyOverloadedFunction.h create mode 100644 wrap/OverloadedFunction.h diff --git a/wrap/Argument.h b/wrap/Argument.h index 02f104418..3d8d7288f 100644 --- a/wrap/Argument.h +++ b/wrap/Argument.h @@ -122,13 +122,5 @@ struct ArgumentList: public std::vector { }; -template -inline void verifyArguments(const std::vector& validArgs, - const std::map& vt) { - typedef typename std::map::value_type NamedMethod; - BOOST_FOREACH(const NamedMethod& namedMethod, vt) - namedMethod.second.verifyArguments(validArgs); -} - } // \namespace wrap diff --git a/wrap/Class.cpp b/wrap/Class.cpp index 3a3432eb3..9c759bb62 100644 --- a/wrap/Class.cpp +++ b/wrap/Class.cpp @@ -286,13 +286,13 @@ void Class::addMethod(bool verbose, bool is_const, Str methodName, // Now stick in new overload stack with expandedMethodName key // but note we use the same, unexpanded methodName in overload string expandedMethodName = methodName + instName.name; - methods[expandedMethodName].addOverload(verbose, is_const, methodName, - expandedArgs, expandedRetVal, instName); + methods[expandedMethodName].addOverload(methodName, expandedArgs, + expandedRetVal, is_const, instName, verbose); } } else // just add overload - methods[methodName].addOverload(verbose, is_const, methodName, argumentList, - returnValue); + methods[methodName].addOverload(methodName, argumentList, returnValue, + is_const, Qualified(), verbose); } /* ************************************************************************* */ diff --git a/wrap/Constructor.h b/wrap/Constructor.h index adfcb8472..870f1a15e 100644 --- a/wrap/Constructor.h +++ b/wrap/Constructor.h @@ -18,7 +18,7 @@ #pragma once -#include "Function.h" +#include "OverloadedFunction.h" #include #include @@ -26,21 +26,17 @@ namespace wrap { // Constructor class -struct Constructor: public ArgumentOverloads { +struct Constructor: public OverloadedFunction { /// Constructor creates an empty class - Constructor(bool verbose = false) : - verbose_(verbose) { + Constructor(bool verbose = false) { + verbose_ = verbose; } - // Then the instance variables are set directly by the Module constructor - std::string name; - bool verbose_; - Constructor expandTemplate(const TemplateSubstitution& ts) const { Constructor inst = *this; inst.argLists_ = expandArgumentListsTemplate(ts); - inst.name = ts.expandedClassName(); + inst.name_ = ts.expandedClassName(); return inst; } @@ -56,7 +52,7 @@ struct Constructor: public ArgumentOverloads { proxyFile.oss << "%\n%-------Constructors-------\n"; for (size_t i = 0; i < nrOverloads(); i++) { proxyFile.oss << "%"; - argumentList(i).emit_prototype(proxyFile, name); + argumentList(i).emit_prototype(proxyFile, name_); proxyFile.oss << "\n"; } } @@ -80,7 +76,7 @@ struct Constructor: public ArgumentOverloads { friend std::ostream& operator<<(std::ostream& os, const Constructor& m) { for (size_t i = 0; i < m.nrOverloads(); i++) - os << m.name << m.argLists_[i]; + os << m.name_ << m.argLists_[i]; return os; } diff --git a/wrap/FullyOverloadedFunction.h b/wrap/FullyOverloadedFunction.h new file mode 100644 index 000000000..ac22ec3a8 --- /dev/null +++ b/wrap/FullyOverloadedFunction.h @@ -0,0 +1,133 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/** + * @file FullyOverloadedFunction.h + * @brief Function that can be fully overloaded: arguments and return values + * @author Frank Dellaert + * @date Nov 13, 2014 + **/ + +#pragma once + +#include "OverloadedFunction.h" + +namespace wrap { + +/** + * Signature Overload (including return value) + */ +class SignatureOverloads: public ArgumentOverloads { + +protected: + + std::vector returnVals_; + +public: + + const ReturnValue& returnValue(size_t i) const { + return returnVals_.at(i); + } + + void push_back(const ArgumentList& args, const ReturnValue& retVal) { + argLists_.push_back(args); + returnVals_.push_back(retVal); + } + + void verifyReturnTypes(const std::vector& validtypes, + const std::string& s) const { + BOOST_FOREACH(const ReturnValue& retval, returnVals_) { + retval.type1.verify(validtypes, s); + if (retval.isPair) + retval.type2.verify(validtypes, s); + } + } + + // TODO use transform ? + std::vector expandReturnValuesTemplate( + const TemplateSubstitution& ts) const { + std::vector result; + BOOST_FOREACH(const ReturnValue& retVal, returnVals_) { + ReturnValue instRetVal = retVal.expandTemplate(ts); + result.push_back(instRetVal); + } + return result; + } + + /// Expand templates, imperative ! + void expandTemplate(const TemplateSubstitution& ts) { + // substitute template in arguments + argLists_ = expandArgumentListsTemplate(ts); + // do the same for return types + returnVals_ = expandReturnValuesTemplate(ts); + } + + // emit a list of comments, one for each overload + void usage_fragment(FileWriter& proxyFile, const std::string& name) const { + unsigned int argLCount = 0; + BOOST_FOREACH(ArgumentList argList, argLists_) { + argList.emit_prototype(proxyFile, name); + if (argLCount != nrOverloads() - 1) + proxyFile.oss << ", "; + else + proxyFile.oss << " : returns " << returnValue(0).return_type(false) + << std::endl; + argLCount++; + } + } + + // emit a list of comments, one for each overload + void comment_fragment(FileWriter& proxyFile, const std::string& name) const { + size_t i = 0; + BOOST_FOREACH(ArgumentList argList, argLists_) { + proxyFile.oss << "%"; + argList.emit_prototype(proxyFile, name); + proxyFile.oss << " : returns " << returnVals_[i++].return_type(false) + << std::endl; + } + } + + friend std::ostream& operator<<(std::ostream& os, + const SignatureOverloads& overloads) { + for (size_t i = 0; i < overloads.nrOverloads(); i++) + os << overloads.returnVals_[i] << overloads.argLists_[i] << std::endl; + return os; + } + +}; + +class FullyOverloadedFunction: public Function, public SignatureOverloads { + +public: + + bool addOverload(const std::string& name, const ArgumentList& args, + const ReturnValue& retVal, const Qualified& instName = Qualified(), + bool verbose = false) { + bool first = initializeOrCheck(name, instName, verbose); + SignatureOverloads::push_back(args, retVal); + return first; + } + +}; + +// Templated checking functions +// TODO: do this via polymorphism, use transform ? + +template +inline void verifyReturnTypes(const std::vector& validTypes, + const std::map& vt) { + typedef typename std::map::value_type NamedMethod; + BOOST_FOREACH(const NamedMethod& namedMethod, vt) + namedMethod.second.verifyReturnTypes(validTypes); +} + +} // \namespace wrap + diff --git a/wrap/Function.cpp b/wrap/Function.cpp index ab3958c62..6faa70fb9 100644 --- a/wrap/Function.cpp +++ b/wrap/Function.cpp @@ -29,38 +29,29 @@ using namespace std; using namespace wrap; /* ************************************************************************* */ -void Function::addOverload(bool verbose, const std::string& name, - const Qualified& instName) { +bool Function::initializeOrCheck(const std::string& name, + const Qualified& instName, bool verbose) { + + if (name.empty()) + throw std::runtime_error( + "Function::initializeOrCheck called with empty name"); // Check if this overload is give to the correct method - if (name_.empty()) + if (name_.empty()) { name_ = name; - else if (name_ != name) - throw std::runtime_error( - "Function::addOverload: tried to add overload with name " + name - + " instead of expected " + name_); - - // Check if this overload is give to the correct method - if (templateArgValue_.empty()) templateArgValue_ = instName; - else if (templateArgValue_ != instName) - throw std::runtime_error( - "Function::addOverload: tried to add overload with template argument " - + instName.qualifiedName(":") + " instead of expected " - + templateArgValue_.qualifiedName(":")); + verbose_ = verbose; + return true; + } else { + if (name_ != name || templateArgValue_ != instName || verbose_ != verbose) + throw std::runtime_error( + "Function::initializeOrCheck called with different arguments: with name " + + name + " instead of expected " + name_ + + ", or with template argument " + instName.qualifiedName(":") + + " instead of expected " + templateArgValue_.qualifiedName(":")); - verbose_ = verbose; -} - -/* ************************************************************************* */ -vector ArgumentOverloads::expandArgumentListsTemplate( - const TemplateSubstitution& ts) const { - vector result; - BOOST_FOREACH(const ArgumentList& argList, argLists_) { - ArgumentList instArgList = argList.expandTemplate(ts); - result.push_back(instArgList); + return false; } - return result; } /* ************************************************************************* */ diff --git a/wrap/Function.h b/wrap/Function.h index 1d40fcbea..49a26bd8d 100644 --- a/wrap/Function.h +++ b/wrap/Function.h @@ -19,11 +19,6 @@ #pragma once #include "Argument.h" -#include "ReturnValue.h" -#include "TypeAttributesTable.h" - -#include -#include namespace wrap { @@ -32,20 +27,18 @@ class Function { protected: - bool verbose_; std::string name_; ///< name of method Qualified templateArgValue_; ///< value of template argument if applicable + bool verbose_; public: - /// Constructor creates empty object - Function(bool verbose = true) : - verbose_(verbose) { - } - - Function(const std::string& name, bool verbose = true) : - verbose_(verbose), name_(name) { - } + /** + * @brief first time, fill in instance variables, otherwise check if same + * @return true if first time, false thereafter + */ + bool initializeOrCheck(const std::string& name, const Qualified& instName = + Qualified(), bool verbose = false); std::string name() const { return name_; @@ -57,172 +50,7 @@ public: else return name_ + templateArgValue_.name; } - - // 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 Qualified& instName = Qualified()); }; -/** - * ArgumentList Overloads - */ -class ArgumentOverloads { - -protected: - - std::vector argLists_; - -public: - - size_t nrOverloads() const { - return argLists_.size(); - } - - const ArgumentList& argumentList(size_t i) const { - return argLists_.at(i); - } - - void addOverload(const ArgumentList& args) { - argLists_.push_back(args); - } - - std::vector expandArgumentListsTemplate( - const TemplateSubstitution& ts) const; - - /// Expand templates, imperative ! - virtual void ExpandTemplate(const TemplateSubstitution& ts) { - argLists_ = expandArgumentListsTemplate(ts); - } - - void verifyArguments(const std::vector& validArgs, - const std::string s) const { - BOOST_FOREACH(const ArgumentList& argList, argLists_) { - BOOST_FOREACH(Argument arg, argList) { - std::string fullType = arg.type.qualifiedName("::"); - if (find(validArgs.begin(), validArgs.end(), fullType) - == validArgs.end()) - throw DependencyMissing(fullType, "checking argument of " + s); - } - } - } - - friend std::ostream& operator<<(std::ostream& os, - const ArgumentOverloads& overloads) { - BOOST_FOREACH(const ArgumentList& argList, overloads.argLists_) - os << argList << std::endl; - return os; - } - -}; - -/** - * Signature Overload (including return value) - */ -class SignatureOverloads: public ArgumentOverloads { - -protected: - - std::vector returnVals_; - -public: - - const ReturnValue& returnValue(size_t i) const { - return returnVals_.at(i); - } - - void addOverload(const ArgumentList& args, const ReturnValue& retVal) { - argLists_.push_back(args); - returnVals_.push_back(retVal); - } - - void verifyReturnTypes(const std::vector& validtypes, - const std::string& s) const { - BOOST_FOREACH(const ReturnValue& retval, returnVals_) { - retval.type1.verify(validtypes, s); - if (retval.isPair) - retval.type2.verify(validtypes, s); - } - } - - // TODO use transform ? - std::vector expandReturnValuesTemplate( - const TemplateSubstitution& ts) const { - std::vector result; - BOOST_FOREACH(const ReturnValue& retVal, returnVals_) { - ReturnValue instRetVal = retVal.expandTemplate(ts); - result.push_back(instRetVal); - } - return result; - } - - /// Expand templates, imperative ! - void expandTemplate(const TemplateSubstitution& ts) { - // substitute template in arguments - argLists_ = expandArgumentListsTemplate(ts); - // do the same for return types - returnVals_ = expandReturnValuesTemplate(ts); - } - - // emit a list of comments, one for each overload - void usage_fragment(FileWriter& proxyFile, const std::string& name) const { - unsigned int argLCount = 0; - BOOST_FOREACH(ArgumentList argList, argLists_) { - argList.emit_prototype(proxyFile, name); - if (argLCount != nrOverloads() - 1) - proxyFile.oss << ", "; - else - proxyFile.oss << " : returns " << returnValue(0).return_type(false) - << std::endl; - argLCount++; - } - } - - // emit a list of comments, one for each overload - void comment_fragment(FileWriter& proxyFile, const std::string& name) const { - size_t i = 0; - BOOST_FOREACH(ArgumentList argList, argLists_) { - proxyFile.oss << "%"; - argList.emit_prototype(proxyFile, name); - proxyFile.oss << " : returns " << returnVals_[i++].return_type(false) - << std::endl; - } - } - - friend std::ostream& operator<<(std::ostream& os, - const SignatureOverloads& overloads) { - for (size_t i = 0; i < overloads.nrOverloads(); i++) - os << overloads.returnVals_[i] << overloads.argLists_[i] << std::endl; - return os; - } - -}; - -// Templated checking functions -// TODO: do this via polymorphism ? - -// TODO use transform -template -static std::map expandMethodTemplate( - const std::map& methods, const TemplateSubstitution& ts) { - std::map result; - typedef std::pair NamedMethod; - BOOST_FOREACH(NamedMethod namedMethod, methods) { - F instMethod = namedMethod.second; - instMethod.expandTemplate(ts); - namedMethod.second = instMethod; - result.insert(namedMethod); - } - return result; -} -template -inline void verifyReturnTypes(const std::vector& validTypes, - const std::map& vt) { - typedef typename std::map::value_type NamedMethod; - BOOST_FOREACH(const NamedMethod& namedMethod, vt) - namedMethod.second.verifyReturnTypes(validTypes); -} - } // \namespace wrap diff --git a/wrap/GlobalFunction.cpp b/wrap/GlobalFunction.cpp index 1f9d6518e..916189632 100644 --- a/wrap/GlobalFunction.cpp +++ b/wrap/GlobalFunction.cpp @@ -16,18 +16,18 @@ namespace wrap { using namespace std; /* ************************************************************************* */ -void GlobalFunction::addOverload(bool verbose, const Qualified& overload, +void GlobalFunction::addOverload(const Qualified& overload, const ArgumentList& args, const ReturnValue& retVal, - const Qualified& instName) { - Function::addOverload(verbose, overload.name, instName); - SignatureOverloads::addOverload(args, retVal); + const Qualified& instName, bool verbose) { + string name(overload.name); + FullyOverloadedFunction::addOverload(name, args, retVal, instName, verbose); overloads.push_back(overload); } /* ************************************************************************* */ -void GlobalFunction::matlab_proxy(const std::string& toolboxPath, - const std::string& wrapperName, const TypeAttributesTable& typeAttributes, - FileWriter& file, std::vector& functionNames) const { +void GlobalFunction::matlab_proxy(const string& toolboxPath, + const string& wrapperName, const TypeAttributesTable& typeAttributes, + FileWriter& file, vector& functionNames) const { // cluster overloads with same namespace // create new GlobalFunction structures around namespaces - same namespaces and names are overloads @@ -40,8 +40,7 @@ void GlobalFunction::matlab_proxy(const std::string& toolboxPath, string str_ns = qualifiedName("", overload.namespaces); const ReturnValue& ret = returnValue(i); const ArgumentList& args = argumentList(i); - grouped_functions[str_ns].addOverload(verbose_, overload, args, ret, - templateArgValue_); + grouped_functions[str_ns].addOverload(overload, args, ret); } size_t lastcheck = grouped_functions.size(); @@ -54,9 +53,9 @@ void GlobalFunction::matlab_proxy(const std::string& toolboxPath, } /* ************************************************************************* */ -void GlobalFunction::generateSingleFunction(const std::string& toolboxPath, - const std::string& wrapperName, const TypeAttributesTable& typeAttributes, - FileWriter& file, std::vector& functionNames) const { +void GlobalFunction::generateSingleFunction(const string& toolboxPath, + const string& wrapperName, const TypeAttributesTable& typeAttributes, + FileWriter& file, vector& functionNames) const { // create the folder for the namespace const Qualified& overload1 = overloads.front(); diff --git a/wrap/GlobalFunction.h b/wrap/GlobalFunction.h index 18bb91995..a086e8154 100644 --- a/wrap/GlobalFunction.h +++ b/wrap/GlobalFunction.h @@ -9,23 +9,18 @@ #pragma once -#include "Function.h" +#include "FullyOverloadedFunction.h" namespace wrap { -struct GlobalFunction: public Function, public SignatureOverloads { +struct GlobalFunction: public FullyOverloadedFunction { std::vector overloads; ///< Stack of qualified names - // Constructor only used in Module - GlobalFunction(bool verbose = true) : - Function(verbose) { - } - - // Used to reconstruct - GlobalFunction(const std::string& name, bool verbose = true) : - Function(name,verbose) { - } + // adds an overloaded version of this function, + void addOverload(const Qualified& overload, const ArgumentList& args, + const ReturnValue& retVal, const Qualified& instName = Qualified(), + bool verbose = false); void verifyArguments(const std::vector& validArgs) const { SignatureOverloads::verifyArguments(validArgs, name_); @@ -35,11 +30,6 @@ struct GlobalFunction: public Function, public SignatureOverloads { SignatureOverloads::verifyReturnTypes(validtypes, name_); } - // adds an overloaded version of this function, - void addOverload(bool verbose, const Qualified& overload, - const ArgumentList& args, const ReturnValue& retVal, - const Qualified& instName = Qualified()); - // codegen function called from Module to build the cpp and matlab versions of the function void matlab_proxy(const std::string& toolboxPath, const std::string& wrapperName, const TypeAttributesTable& typeAttributes, diff --git a/wrap/Method.cpp b/wrap/Method.cpp index a7072c9e7..49d90378d 100644 --- a/wrap/Method.cpp +++ b/wrap/Method.cpp @@ -29,17 +29,25 @@ using namespace std; using namespace wrap; /* ************************************************************************* */ -void Method::addOverload(bool verbose, bool is_const, Str name, - const ArgumentList& args, const ReturnValue& retVal, - const Qualified& instName) { - - StaticMethod::addOverload(verbose, name, args, retVal, instName); - is_const_ = is_const; +bool Method::addOverload(Str name, const ArgumentList& args, + const ReturnValue& retVal, bool is_const, const Qualified& instName, + bool verbose) { + bool first = StaticMethod::addOverload(name, args, retVal, instName, verbose); + if (first) + is_const_ = is_const; + else if (is_const && !is_const_) + throw std::runtime_error( + "Method::addOverload now designated as const whereas before it was not"); + else if (!is_const && is_const_) + throw std::runtime_error( + "Method::addOverload now designated as non-const whereas before it was"); + return first; } /* ************************************************************************* */ void Method::proxy_header(FileWriter& proxyFile) const { - proxyFile.oss << " function varargout = " << matlabName() << "(this, varargin)\n"; + proxyFile.oss << " function varargout = " << matlabName() + << "(this, varargin)\n"; } /* ************************************************************************* */ diff --git a/wrap/Method.h b/wrap/Method.h index 13847700d..db9e6bb9f 100644 --- a/wrap/Method.h +++ b/wrap/Method.h @@ -31,14 +31,9 @@ public: typedef const std::string& Str; - /// Constructor creates empty object - Method(bool verbose = true) : - StaticMethod(verbose), is_const_(false) { - } - - Method(const std::string& name, bool verbose = true) : - StaticMethod(name,verbose), is_const_(false) { - } + bool addOverload(Str name, const ArgumentList& args, + const ReturnValue& retVal, bool is_const, const Qualified& instName = + Qualified(), bool verbose = false); virtual bool isStatic() const { return false; @@ -48,13 +43,6 @@ public: return is_const_; } - // 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, Str name, - const ArgumentList& args, const ReturnValue& retVal, - const Qualified& instName = Qualified()); - friend std::ostream& operator<<(std::ostream& os, const Method& m) { for (size_t i = 0; i < m.nrOverloads(); i++) os << m.returnVals_[i] << " " << m.name_ << m.argLists_[i]; diff --git a/wrap/Module.cpp b/wrap/Module.cpp index 2fc8f92bc..2f35f0bbf 100644 --- a/wrap/Module.cpp +++ b/wrap/Module.cpp @@ -217,7 +217,7 @@ void Module::parseMarkup(const std::string& data) { Constructor constructor0(verbose), constructor(verbose); Rule constructor_p = (className_p >> '(' >> argumentList_p >> ')' >> ';' >> !comments_p) - [bl::bind(&Constructor::addOverload, bl::var(constructor), bl::var(args))] + [bl::bind(&Constructor::push_back, bl::var(constructor), bl::var(args))] [clear_a(args)]; vector namespaces_return; /// namespace for current return type @@ -274,7 +274,7 @@ void Module::parseMarkup(const std::string& data) { '(' >> argumentList_p >> ')' >> ';' >> *comments_p) [bl::bind(&StaticMethod::addOverload, bl::var(cls.static_methods)[bl::var(methodName)], - verbose, bl::var(methodName), bl::var(args), bl::var(retVal), Qualified())] + bl::var(methodName), bl::var(args), bl::var(retVal), Qualified(),verbose)] [assign_a(retVal,retVal0)] [clear_a(args)]; @@ -295,7 +295,8 @@ void Module::parseMarkup(const std::string& data) { >> ((':' >> classParent_p >> '{') | '{') >> *(functions_p | comments_p) >> str_p("};")) - [assign_a(constructor.name, cls.name)] + [bl::bind(&Constructor::initializeOrCheck, bl::var(constructor), + bl::var(cls.name), Qualified(), verbose)] [assign_a(cls.constructor, constructor)] [assign_a(cls.namespaces, namespaces)] [assign_a(cls.deconstructor.name,cls.name)] @@ -313,7 +314,7 @@ void Module::parseMarkup(const std::string& data) { [assign_a(globalFunction.namespaces,namespaces)] [bl::bind(&GlobalFunction::addOverload, bl::var(global_functions)[bl::var(globalFunction.name)], - verbose, bl::var(globalFunction), bl::var(args), bl::var(retVal), Qualified())] + bl::var(globalFunction), bl::var(args), bl::var(retVal), Qualified(),verbose)] [assign_a(retVal,retVal0)] [clear_a(globalFunction)] [clear_a(args)]; diff --git a/wrap/OverloadedFunction.h b/wrap/OverloadedFunction.h new file mode 100644 index 000000000..47c418748 --- /dev/null +++ b/wrap/OverloadedFunction.h @@ -0,0 +1,126 @@ +/* ---------------------------------------------------------------------------- + + * GTSAM Copyright 2010, Georgia Tech Research Corporation, + * Atlanta, Georgia 30332-0415 + * All Rights Reserved + * Authors: Frank Dellaert, et al. (see THANKS for the full author list) + + * See LICENSE for the license information + + * -------------------------------------------------------------------------- */ + +/** + * @file OverloadedFunction.h + * @brief Function that can overload its arguments only + * @author Frank Dellaert + * @date Nov 13, 2014 + **/ + +#pragma once + +#include "Function.h" +#include "Argument.h" + +namespace wrap { + +/** + * ArgumentList Overloads + */ +class ArgumentOverloads { + +protected: + + std::vector argLists_; + +public: + + size_t nrOverloads() const { + return argLists_.size(); + } + + const ArgumentList& argumentList(size_t i) const { + return argLists_.at(i); + } + + void push_back(const ArgumentList& args) { + argLists_.push_back(args); + } + + std::vector expandArgumentListsTemplate( + const TemplateSubstitution& ts) const { + std::vector result; + BOOST_FOREACH(const ArgumentList& argList, argLists_) { + ArgumentList instArgList = argList.expandTemplate(ts); + result.push_back(instArgList); + } + return result; + } + + /// Expand templates, imperative ! + virtual void ExpandTemplate(const TemplateSubstitution& ts) { + argLists_ = expandArgumentListsTemplate(ts); + } + + void verifyArguments(const std::vector& validArgs, + const std::string s) const { + BOOST_FOREACH(const ArgumentList& argList, argLists_) { + BOOST_FOREACH(Argument arg, argList) { + std::string fullType = arg.type.qualifiedName("::"); + if (find(validArgs.begin(), validArgs.end(), fullType) + == validArgs.end()) + throw DependencyMissing(fullType, "checking argument of " + s); + } + } + } + + friend std::ostream& operator<<(std::ostream& os, + const ArgumentOverloads& overloads) { + BOOST_FOREACH(const ArgumentList& argList, overloads.argLists_) + os << argList << std::endl; + return os; + } + +}; + +class OverloadedFunction: public Function, public ArgumentOverloads { + +public: + + bool addOverload(const std::string& name, const ArgumentList& args, + const Qualified& instName = Qualified(), bool verbose = false) { + bool first = initializeOrCheck(name, instName, verbose); + ArgumentOverloads::push_back(args); + return first; + } + +private: + +}; + +// Templated checking functions +// TODO: do this via polymorphism, use transform ? + +template +static std::map expandMethodTemplate( + const std::map& methods, const TemplateSubstitution& ts) { + std::map result; + typedef std::pair NamedMethod; + BOOST_FOREACH(NamedMethod namedMethod, methods) { + F instMethod = namedMethod.second; + instMethod.expandTemplate(ts); + namedMethod.second = instMethod; + result.insert(namedMethod); + } + return result; +} + +template +inline void verifyArguments(const std::vector& validArgs, + const std::map& vt) { + typedef typename std::map::value_type NamedMethod; + BOOST_FOREACH(const NamedMethod& namedMethod, vt) + namedMethod.second.verifyArguments(validArgs); +} + +} // \namespace wrap + diff --git a/wrap/StaticMethod.cpp b/wrap/StaticMethod.cpp index d3bd75628..83cf621b4 100644 --- a/wrap/StaticMethod.cpp +++ b/wrap/StaticMethod.cpp @@ -29,14 +29,6 @@ using namespace std; using namespace wrap; -/* ************************************************************************* */ -void StaticMethod::addOverload(bool verbose, Str name, const ArgumentList& args, - const ReturnValue& retVal, const Qualified& instName) { - - Function::addOverload(verbose, name, instName); - SignatureOverloads::addOverload(args, retVal); -} - /* ************************************************************************* */ void StaticMethod::proxy_header(FileWriter& proxyFile) const { string upperName = matlabName(); diff --git a/wrap/StaticMethod.h b/wrap/StaticMethod.h index 4a6fedbfc..06f98092f 100644 --- a/wrap/StaticMethod.h +++ b/wrap/StaticMethod.h @@ -19,31 +19,19 @@ #pragma once -#include "Function.h" +#include "FullyOverloadedFunction.h" namespace wrap { /// StaticMethod class -struct StaticMethod: public Function, public SignatureOverloads { +struct StaticMethod: public FullyOverloadedFunction { typedef const std::string& Str; - /// Constructor creates empty object - StaticMethod(bool verbosity = true) : - Function(verbosity) { - } - - StaticMethod(const std::string& name, bool verbose = true) : - Function(name,verbose) { - } - virtual bool isStatic() const { return true; } - void addOverload(bool verbose, Str name, const ArgumentList& args, - const ReturnValue& retVal, const Qualified& instName); - // emit a list of comments, one for each overload void comment_fragment(FileWriter& proxyFile) const { SignatureOverloads::comment_fragment(proxyFile, matlabName()); diff --git a/wrap/tests/testMethod.cpp b/wrap/tests/testMethod.cpp index 4067f3d85..8050f0d3c 100644 --- a/wrap/tests/testMethod.cpp +++ b/wrap/tests/testMethod.cpp @@ -32,12 +32,13 @@ TEST( Method, Constructor ) { /* ************************************************************************* */ // addOverload TEST( Method, addOverload ) { - Method method("myName"); - bool verbose = true, is_const = true; + Method method; + method.initializeOrCheck("myName"); + bool is_const = true; ArgumentList args; const ReturnValue retVal(ReturnType("return_type")); - method.addOverload(verbose, is_const, "myName", args, retVal); - EXPECT_LONGS_EQUAL(1,method.nrOverloads()); + method.addOverload("myName", args, retVal, is_const); + EXPECT_LONGS_EQUAL(1, method.nrOverloads()); } /* ************************************************************************* */ From e07402a58a77ad99856e665019111b078ca14da4 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 14 Nov 2014 17:04:45 +0100 Subject: [PATCH 2/4] Re-factored matlab_code only emits code: it does not post-process the classes anymore. That is now done in parse_Markup, i.e., the constructor.... --- wrap/Module.cpp | 70 ++++++++++++++++++++--------------------- wrap/Module.h | 50 +++++++++++++++++------------ wrap/tests/testWrap.cpp | 15 +++++---- 3 files changed, 71 insertions(+), 64 deletions(-) diff --git a/wrap/Module.cpp b/wrap/Module.cpp index 2f35f0bbf..3c228bd4a 100644 --- a/wrap/Module.cpp +++ b/wrap/Module.cpp @@ -394,51 +394,35 @@ void Module::parseMarkup(const std::string& data) { // Explicitly add methods to the classes from parents so it shows in documentation BOOST_FOREACH(Class& cls, classes) cls.appendInheritedMethods(cls, classes); -} - -/* ************************************************************************* */ -void Module::generateIncludes(FileWriter& file) const { - - // collect includes - vector all_includes(includes); - - // sort and remove duplicates - sort(all_includes.begin(), all_includes.end()); - vector::const_iterator last_include = unique(all_includes.begin(), all_includes.end()); - vector::const_iterator it = all_includes.begin(); - // add includes to file - for (; it != last_include; ++it) - file.oss << "#include <" << *it << ">" << endl; - file.oss << "\n"; -} - - -/* ************************************************************************* */ -void Module::matlab_code(const string& toolboxPath, const string& headerPath) const { - - fs::create_directories(toolboxPath); // Expand templates - This is done first so that template instantiations are // counted in the list of valid types, have their attributes and dependencies // checked, etc. - vector expandedClasses = ExpandTypedefInstantiations(classes, templateInstantiationTypedefs); + expandedClasses = ExpandTypedefInstantiations(classes, + templateInstantiationTypedefs); // Dependency check list - vector validTypes = GenerateValidTypes(expandedClasses, forward_declarations); + vector validTypes = GenerateValidTypes(expandedClasses, + forward_declarations); // Check that all classes have been defined somewhere verifyArguments(validTypes, global_functions); verifyReturnTypes(validTypes, global_functions); - bool hasSerialiable = false; + hasSerialiable = false; BOOST_FOREACH(const Class& cls, expandedClasses) cls.verifyAll(validTypes,hasSerialiable); // Create type attributes table and check validity - TypeAttributesTable typeAttributes; typeAttributes.addClasses(expandedClasses); typeAttributes.addForwardDeclarations(forward_declarations); typeAttributes.checkValidity(expandedClasses); +} + +/* ************************************************************************* */ +void Module::matlab_code(const string& toolboxPath) const { + + fs::create_directories(toolboxPath); // create the unified .cpp switch file const string wrapperName = name + "_wrapper"; @@ -459,19 +443,18 @@ void Module::matlab_code(const string& toolboxPath, const string& headerPath) co // Generate includes while avoiding redundant includes generateIncludes(wrapperFile); - // create typedef classes - we put this at the top of the wrap file so that collectors and method arguments can use these typedefs - BOOST_FOREACH(const Class& cls, expandedClasses) { + // create typedef classes - we put this at the top of the wrap file so that + // collectors and method arguments can use these typedefs + BOOST_FOREACH(const Class& cls, expandedClasses) if(!cls.typedefName.empty()) wrapperFile.oss << cls.getTypedef() << "\n"; - } wrapperFile.oss << "\n"; // Generate boost.serialization export flags (needs typedefs from above) if (hasSerialiable) { - BOOST_FOREACH(const Class& cls, expandedClasses) { + BOOST_FOREACH(const Class& cls, expandedClasses) if(cls.isSerializable) wrapperFile.oss << cls.getSerializationExport() << "\n"; - } wrapperFile.oss << "\n"; } @@ -484,14 +467,12 @@ void Module::matlab_code(const string& toolboxPath, const string& headerPath) co vector functionNames; // Function names stored by index for switch // create proxy class and wrapper code - BOOST_FOREACH(const Class& cls, expandedClasses) { + BOOST_FOREACH(const Class& cls, expandedClasses) cls.matlab_proxy(toolboxPath, wrapperName, typeAttributes, wrapperFile, functionNames); - } // create matlab files and wrapper code for global functions - BOOST_FOREACH(const GlobalFunctions::value_type& p, global_functions) { + BOOST_FOREACH(const GlobalFunctions::value_type& p, global_functions) p.second.matlab_proxy(toolboxPath, wrapperName, typeAttributes, wrapperFile, functionNames); - } // finish wrapper file wrapperFile.oss << "\n"; @@ -501,6 +482,23 @@ void Module::matlab_code(const string& toolboxPath, const string& headerPath) co } /* ************************************************************************* */ +void Module::generateIncludes(FileWriter& file) const { + + // collect includes + vector all_includes(includes); + + // sort and remove duplicates + sort(all_includes.begin(), all_includes.end()); + vector::const_iterator last_include = unique(all_includes.begin(), all_includes.end()); + vector::const_iterator it = all_includes.begin(); + // add includes to file + for (; it != last_include; ++it) + file.oss << "#include <" << *it << ">" << endl; + file.oss << "\n"; +} + + +/* ************************************************************************* */ void Module::finish_wrapper(FileWriter& file, const std::vector& functionNames) const { file.oss << "void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[])\n"; file.oss << "{\n"; diff --git a/wrap/Module.h b/wrap/Module.h index 8ef2bc4fd..1709719d1 100644 --- a/wrap/Module.h +++ b/wrap/Module.h @@ -37,40 +37,50 @@ struct Module { typedef std::map GlobalFunctions; typedef std::map Methods; - std::string name; ///< module name - bool verbose; ///< verbose flag + // Filled during parsing: + std::string name; ///< module name + bool verbose; ///< verbose flag std::vector classes; ///< list of classes std::vector templateInstantiationTypedefs; ///< list of template instantiations std::vector forward_declarations; - std::vector includes; ///< Include statements + std::vector includes; ///< Include statements GlobalFunctions global_functions; + // After parsing: + std::vector expandedClasses; + bool hasSerialiable; + TypeAttributesTable typeAttributes; + /// constructor that parses interface file - Module(const std::string& interfacePath, - const std::string& moduleName, - bool enable_verbose=true); + Module(const std::string& interfacePath, const std::string& moduleName, + bool enable_verbose = true); /// Dummy constructor that does no parsing - use only for testing - Module(const std::string& moduleName, bool enable_verbose=true); - - /// MATLAB code generation: - void matlab_code( - const std::string& path, - const std::string& headerPath) const; // FIXME: headerPath not actually used? - - void finish_wrapper(FileWriter& file, const std::vector& functionNames) const; - - void generateIncludes(FileWriter& file) const; + Module(const std::string& moduleName, bool enable_verbose = true); /// non-const function that performs parsing - typically called by constructor /// Throws exception on failure void parseMarkup(const std::string& data); + /// MATLAB code generation: + void matlab_code(const std::string& path) const; + + void generateIncludes(FileWriter& file) const; + + void finish_wrapper(FileWriter& file, + const std::vector& functionNames) const; + private: - static std::vector ExpandTypedefInstantiations(const std::vector& classes, const std::vector instantiations); - static std::vector GenerateValidTypes(const std::vector& classes, const std::vector forwardDeclarations); - static void WriteCollectorsAndCleanupFcn(FileWriter& wrapperFile, const std::string& moduleName, const std::vector& classes); - static void WriteRTTIRegistry(FileWriter& wrapperFile, const std::string& moduleName, const std::vector& classes); + static std::vector ExpandTypedefInstantiations( + const std::vector& classes, + const std::vector instantiations); + static std::vector GenerateValidTypes( + const std::vector& classes, + const std::vector forwardDeclarations); + static void WriteCollectorsAndCleanupFcn(FileWriter& wrapperFile, + const std::string& moduleName, const std::vector& classes); + static void WriteRTTIRegistry(FileWriter& wrapperFile, + const std::string& moduleName, const std::vector& classes); }; } // \namespace wrap diff --git a/wrap/tests/testWrap.cpp b/wrap/tests/testWrap.cpp index 8fe862182..003801b05 100644 --- a/wrap/tests/testWrap.cpp +++ b/wrap/tests/testWrap.cpp @@ -64,12 +64,11 @@ TEST( wrap, check_exception ) { THROWS_EXCEPTION(Module("/notarealpath", "geometry",enable_verbose)); CHECK_EXCEPTION(Module("/alsonotarealpath", "geometry",enable_verbose), CantOpenFile); - // clean out previous generated code - fs::remove_all("actual_deps"); - - string path = topdir + "/wrap/tests"; - Module module(path.c_str(), "testDependencies",enable_verbose); - CHECK_EXCEPTION(module.matlab_code("actual_deps", headerPath), DependencyMissing); +// // TODO: matlab_code does not throw this anymore, so check constructor +// fs::remove_all("actual_deps"); // clean out previous generated code +// string path = topdir + "/wrap/tests"; +// Module module(path.c_str(), "testDependencies",enable_verbose); +// CHECK_EXCEPTION(module.matlab_code("actual_deps"), DependencyMissing); } /* ************************************************************************* */ @@ -413,7 +412,7 @@ TEST( wrap, matlab_code_namespaces ) { // emit MATLAB code string exp_path = path + "/tests/expected_namespaces/"; string act_path = "actual_namespaces/"; - module.matlab_code("actual_namespaces", headerPath); + module.matlab_code("actual_namespaces"); EXPECT(files_equal(exp_path + "ClassD.m", act_path + "ClassD.m" )); @@ -441,7 +440,7 @@ TEST( wrap, matlab_code_geometry ) { // emit MATLAB code // make_geometry will not compile, use make testwrap to generate real make - module.matlab_code("actual", headerPath); + module.matlab_code("actual"); #ifndef WRAP_DISABLE_SERIALIZE string epath = path + "/tests/expected/"; #else From 6c24fc2aca018d37143b8182a11eac2ee4a9db2c Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 14 Nov 2014 17:47:25 +0100 Subject: [PATCH 3/4] Python prototype --- wrap/Class.cpp | 12 +++++++ wrap/Class.h | 3 ++ wrap/Constructor.cpp | 74 +++++++++++++++++++++++++---------------- wrap/Constructor.h | 18 ++++++---- wrap/GlobalFunction.cpp | 5 +++ wrap/GlobalFunction.h | 3 ++ wrap/Module.cpp | 28 ++++++++++++++++ wrap/Module.h | 3 ++ wrap/StaticMethod.cpp | 7 ++++ wrap/StaticMethod.h | 3 ++ wrap/tests/testWrap.cpp | 19 +++++++++++ 11 files changed, 139 insertions(+), 36 deletions(-) diff --git a/wrap/Class.cpp b/wrap/Class.cpp index 9c759bb62..0e480f0fd 100644 --- a/wrap/Class.cpp +++ b/wrap/Class.cpp @@ -583,4 +583,16 @@ string Class::getSerializationExport() const { return "BOOST_CLASS_EXPORT_GUID(" + qualifiedName("::") + ", \"" + qualifiedName() + "\");"; } + +/* ************************************************************************* */ +void Class::python_wrapper(FileWriter& wrapperFile) const { + wrapperFile.oss << "class_<" << name << ">(\"" << name << "\")\n"; + constructor.python_wrapper(wrapperFile, name); + BOOST_FOREACH(const StaticMethod& m, static_methods | boost::adaptors::map_values) + m.python_wrapper(wrapperFile, name); + BOOST_FOREACH(const Method& m, methods | boost::adaptors::map_values) + m.python_wrapper(wrapperFile, name); + wrapperFile.oss << ";\n\n"; +} + /* ************************************************************************* */ diff --git a/wrap/Class.h b/wrap/Class.h index 6b9119316..34323b797 100644 --- a/wrap/Class.h +++ b/wrap/Class.h @@ -111,6 +111,9 @@ public: void deserialization_fragments(FileWriter& proxyFile, FileWriter& wrapperFile, Str wrapperName, std::vector& functionNames) const; + // emit python wrapper + void python_wrapper(FileWriter& wrapperFile) const; + friend std::ostream& operator<<(std::ostream& os, const Class& cls) { os << "class " << cls.name << "{\n"; os << cls.constructor << ";\n"; diff --git a/wrap/Constructor.cpp b/wrap/Constructor.cpp index fdbbf0e42..782c0ca80 100644 --- a/wrap/Constructor.cpp +++ b/wrap/Constructor.cpp @@ -29,52 +29,55 @@ using namespace std; using namespace wrap; - /* ************************************************************************* */ -string Constructor::matlab_wrapper_name(const string& className) const { +string Constructor::matlab_wrapper_name(Str className) const { string str = "new_" + className; return str; } /* ************************************************************************* */ -void Constructor::proxy_fragment(FileWriter& file, const std::string& wrapperName, - bool hasParent, const int id, const ArgumentList args) const { +void Constructor::proxy_fragment(FileWriter& file, + const std::string& wrapperName, bool hasParent, const int id, + const ArgumentList args) const { size_t nrArgs = args.size(); // check for number of arguments... file.oss << " elseif nargin == " << nrArgs; - if (nrArgs>0) file.oss << " && "; + if (nrArgs > 0) + file.oss << " && "; // ...and their types bool first = true; - for(size_t i=0;i(id); + const string wrapFunctionName = matlabUniqueName + "_constructor_" + + boost::lexical_cast(id); - file.oss << "void " << wrapFunctionName << "(int nargout, mxArray *out[], int nargin, const mxArray *in[])" << endl; + file.oss << "void " << wrapFunctionName + << "(int nargout, mxArray *out[], int nargin, const mxArray *in[])" + << endl; file.oss << "{\n"; file.oss << " mexAtExit(&_deleteAllObjects);\n"; //Typedef boost::shared_ptr @@ -82,22 +85,29 @@ string Constructor::wrapper_fragment(FileWriter& file, file.oss << "\n"; //Check to see if there will be any arguments and remove {} for consiseness - if(al.size() > 0) + if (al.size() > 0) al.matlab_unwrap(file); // unwrap arguments - file.oss << " Shared *self = new Shared(new " << cppClassName << "(" << al.names() << "));" << endl; + file.oss << " Shared *self = new Shared(new " << cppClassName << "(" + << al.names() << "));" << endl; file.oss << " collector_" << matlabUniqueName << ".insert(self);\n"; - if(verbose_) + if (verbose_) file.oss << " std::cout << \"constructed \" << self << std::endl;" << endl; - file.oss << " out[0] = mxCreateNumericMatrix(1, 1, mxUINT32OR64_CLASS, mxREAL);" << endl; - file.oss << " *reinterpret_cast (mxGetData(out[0])) = self;" << endl; + file.oss + << " out[0] = mxCreateNumericMatrix(1, 1, mxUINT32OR64_CLASS, mxREAL);" + << endl; + file.oss << " *reinterpret_cast (mxGetData(out[0])) = self;" + << endl; // If we have a base class, return the base class pointer (MATLAB will call the base class collectorInsertAndMakeBase to add this to the collector and recurse the heirarchy) - if(!cppBaseClassName.empty()) { + if (!cppBaseClassName.empty()) { file.oss << "\n"; - file.oss << " typedef boost::shared_ptr<" << cppBaseClassName << "> SharedBase;\n"; - file.oss << " out[1] = mxCreateNumericMatrix(1, 1, mxUINT32OR64_CLASS, mxREAL);\n"; - file.oss << " *reinterpret_cast(mxGetData(out[1])) = new SharedBase(*self);\n"; + file.oss << " typedef boost::shared_ptr<" << cppBaseClassName + << "> SharedBase;\n"; + file.oss + << " out[1] = mxCreateNumericMatrix(1, 1, mxUINT32OR64_CLASS, mxREAL);\n"; + file.oss + << " *reinterpret_cast(mxGetData(out[1])) = new SharedBase(*self);\n"; } file.oss << "}" << endl; @@ -106,3 +116,9 @@ string Constructor::wrapper_fragment(FileWriter& file, } /* ************************************************************************* */ +void Constructor::python_wrapper(FileWriter& wrapperFile, Str className) const { + wrapperFile.oss << " .def(\"" << name_ << "\", &" << className << "::" << name_ + << ");\n"; +} + +/* ************************************************************************* */ diff --git a/wrap/Constructor.h b/wrap/Constructor.h index 870f1a15e..a026bf423 100644 --- a/wrap/Constructor.h +++ b/wrap/Constructor.h @@ -28,6 +28,8 @@ namespace wrap { // Constructor class struct Constructor: public OverloadedFunction { + typedef const std::string& Str; + /// Constructor creates an empty class Constructor(bool verbose = false) { verbose_ = verbose; @@ -45,7 +47,7 @@ struct Constructor: public OverloadedFunction { // classFile is class proxy file, e.g., ../matlab/@Point2/Point2.m /// wrapper name - std::string matlab_wrapper_name(const std::string& className) const; + std::string matlab_wrapper_name(Str className) const; void comment_fragment(FileWriter& proxyFile) const { if (nrOverloads() > 0) @@ -61,19 +63,21 @@ struct Constructor: public OverloadedFunction { * Create fragment to select constructor in proxy class, e.g., * if nargin == 2, obj.self = new_Pose3_RP(varargin{1},varargin{2}); end */ - void proxy_fragment(FileWriter& file, const std::string& wrapperName, - bool hasParent, const int id, const ArgumentList args) const; + void proxy_fragment(FileWriter& file, Str wrapperName, bool hasParent, + const int id, const ArgumentList args) const; /// cpp wrapper - std::string wrapper_fragment(FileWriter& file, - const std::string& cppClassName, const std::string& matlabUniqueName, - const std::string& cppBaseClassName, int id, + std::string wrapper_fragment(FileWriter& file, Str cppClassName, + Str matlabUniqueName, Str cppBaseClassName, int id, const ArgumentList& al) const; /// constructor function - void generate_construct(FileWriter& file, const std::string& cppClassName, + void generate_construct(FileWriter& file, Str cppClassName, std::vector& args_list) const; + // emit python wrapper + void python_wrapper(FileWriter& wrapperFile, Str className) const; + friend std::ostream& operator<<(std::ostream& os, const Constructor& m) { for (size_t i = 0; i < m.nrOverloads(); i++) os << m.name_ << m.argLists_[i]; diff --git a/wrap/GlobalFunction.cpp b/wrap/GlobalFunction.cpp index 916189632..e843481a1 100644 --- a/wrap/GlobalFunction.cpp +++ b/wrap/GlobalFunction.cpp @@ -127,6 +127,11 @@ void GlobalFunction::generateSingleFunction(const string& toolboxPath, mfunctionFile.emit(true); } +/* ************************************************************************* */ +void GlobalFunction::python_wrapper(FileWriter& wrapperFile) const { + wrapperFile.oss << "def(\"" << name_ << "\", " << name_ << ");\n"; +} + /* ************************************************************************* */ } // \namespace wrap diff --git a/wrap/GlobalFunction.h b/wrap/GlobalFunction.h index a086e8154..6a49fe5ce 100644 --- a/wrap/GlobalFunction.h +++ b/wrap/GlobalFunction.h @@ -35,6 +35,9 @@ struct GlobalFunction: public FullyOverloadedFunction { const std::string& wrapperName, const TypeAttributesTable& typeAttributes, FileWriter& file, std::vector& functionNames) const; + // emit python wrapper + void python_wrapper(FileWriter& wrapperFile) const; + private: // Creates a single global function - all in same namespace diff --git a/wrap/Module.cpp b/wrap/Module.cpp index 3c228bd4a..ac0e0a579 100644 --- a/wrap/Module.cpp +++ b/wrap/Module.cpp @@ -650,3 +650,31 @@ void Module::WriteRTTIRegistry(FileWriter& wrapperFile, const std::string& modul } /* ************************************************************************* */ +void Module::python_wrapper(const string& toolboxPath) const { + + fs::create_directories(toolboxPath); + + // create the unified .cpp switch file + const string wrapperName = name + "_python"; + string wrapperFileName = toolboxPath + "/" + wrapperName + ".cpp"; + FileWriter wrapperFile(wrapperFileName, verbose, "//"); + wrapperFile.oss << "#include \n\n"; + wrapperFile.oss << "using namespace boost::python;\n"; + wrapperFile.oss << "BOOST_PYTHON_MODULE(" + name + ")\n"; + wrapperFile.oss << "{\n"; + + // write out classes + BOOST_FOREACH(const Class& cls, expandedClasses) + cls.python_wrapper(wrapperFile); + + // write out global functions + BOOST_FOREACH(const GlobalFunctions::value_type& p, global_functions) + p.second.python_wrapper(wrapperFile); + + // finish wrapper file + wrapperFile.oss << "}\n"; + + wrapperFile.emit(true); +} + +/* ************************************************************************* */ diff --git a/wrap/Module.h b/wrap/Module.h index 1709719d1..a4659dc3a 100644 --- a/wrap/Module.h +++ b/wrap/Module.h @@ -70,6 +70,9 @@ struct Module { void finish_wrapper(FileWriter& file, const std::vector& functionNames) const; + /// Python code generation: + void python_wrapper(const std::string& path) const; + private: static std::vector ExpandTypedefInstantiations( const std::vector& classes, diff --git a/wrap/StaticMethod.cpp b/wrap/StaticMethod.cpp index 83cf621b4..5f91a15b4 100644 --- a/wrap/StaticMethod.cpp +++ b/wrap/StaticMethod.cpp @@ -165,3 +165,10 @@ string StaticMethod::wrapper_call(FileWriter& wrapperFile, Str cppClassName, } /* ************************************************************************* */ +void StaticMethod::python_wrapper(FileWriter& wrapperFile, + Str className) const { + wrapperFile.oss << " .def(\"" << name_ << "\", &" << className << "::" << name_ + << ");\n"; +} + +/* ************************************************************************* */ diff --git a/wrap/StaticMethod.h b/wrap/StaticMethod.h index 06f98092f..de8e4a94e 100644 --- a/wrap/StaticMethod.h +++ b/wrap/StaticMethod.h @@ -52,6 +52,9 @@ struct StaticMethod: public FullyOverloadedFunction { Str wrapperName, const TypeAttributesTable& typeAttributes, std::vector& functionNames) const; + // emit python wrapper + void python_wrapper(FileWriter& wrapperFile, Str className) const; + friend std::ostream& operator<<(std::ostream& os, const StaticMethod& m) { for (size_t i = 0; i < m.nrOverloads(); i++) os << "static " << m.returnVals_[i] << " " << m.name_ << m.argLists_[i]; diff --git a/wrap/tests/testWrap.cpp b/wrap/tests/testWrap.cpp index 003801b05..0645fb407 100644 --- a/wrap/tests/testWrap.cpp +++ b/wrap/tests/testWrap.cpp @@ -460,6 +460,25 @@ TEST( wrap, matlab_code_geometry ) { EXPECT(files_equal(epath + "overloadedGlobalFunction.m" , apath + "overloadedGlobalFunction.m" )); } +/* ************************************************************************* */ +TEST( wrap, python_code_geometry ) { + // Parse into class object + string header_path = topdir + "/wrap/tests"; + Module module(header_path,"geometry",enable_verbose); + string path = topdir + "/wrap"; + + // clean out previous generated code + fs::remove_all("actual-python"); + + // emit MATLAB code + // make_geometry will not compile, use make testwrap to generate real make + module.python_wrapper("actual-python"); + string epath = path + "/tests/expected-python/"; + string apath = "actual-python/"; + + EXPECT(files_equal(epath + "geometry_python.cpp", apath + "geometry_python.cpp" )); +} + /* ************************************************************************* */ int main() { TestResult tr; return TestRegistry::runAllTests(tr); } /* ************************************************************************* */ From 755cc60b6faa327fb4723cf32f8fc82ce4458cc8 Mon Sep 17 00:00:00 2001 From: dellaert Date: Fri, 14 Nov 2014 17:47:46 +0100 Subject: [PATCH 4/4] python wrapper file generated at this point --- .../tests/expected-python/geometry_python.cpp | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 wrap/tests/expected-python/geometry_python.cpp diff --git a/wrap/tests/expected-python/geometry_python.cpp b/wrap/tests/expected-python/geometry_python.cpp new file mode 100644 index 000000000..f500f2984 --- /dev/null +++ b/wrap/tests/expected-python/geometry_python.cpp @@ -0,0 +1,83 @@ +#include + +using namespace boost::python; +BOOST_PYTHON_MODULE(geometry) +{ +class_("Point2") + .def("Point2", &Point2::Point2); + .def("argChar", &Point2::argChar); + .def("argUChar", &Point2::argUChar); + .def("dim", &Point2::dim); + .def("returnChar", &Point2::returnChar); + .def("vectorConfusion", &Point2::vectorConfusion); + .def("x", &Point2::x); + .def("y", &Point2::y); +; + +class_("Point3") + .def("Point3", &Point3::Point3); + .def("StaticFunctionRet", &Point3::StaticFunctionRet); + .def("staticFunction", &Point3::staticFunction); + .def("norm", &Point3::norm); +; + +class_("Test") + .def("Test", &Test::Test); + .def("arg_EigenConstRef", &Test::arg_EigenConstRef); + .def("create_MixedPtrs", &Test::create_MixedPtrs); + .def("create_ptrs", &Test::create_ptrs); + .def("print", &Test::print); + .def("return_Point2Ptr", &Test::return_Point2Ptr); + .def("return_Test", &Test::return_Test); + .def("return_TestPtr", &Test::return_TestPtr); + .def("return_bool", &Test::return_bool); + .def("return_double", &Test::return_double); + .def("return_field", &Test::return_field); + .def("return_int", &Test::return_int); + .def("return_matrix1", &Test::return_matrix1); + .def("return_matrix2", &Test::return_matrix2); + .def("return_pair", &Test::return_pair); + .def("return_ptrs", &Test::return_ptrs); + .def("return_size_t", &Test::return_size_t); + .def("return_string", &Test::return_string); + .def("return_vector1", &Test::return_vector1); + .def("return_vector2", &Test::return_vector2); +; + +class_("MyBase") + .def("MyBase", &MyBase::MyBase); +; + +class_("MyTemplatePoint2") + .def("MyTemplatePoint2", &MyTemplatePoint2::MyTemplatePoint2); + .def("accept_T", &MyTemplatePoint2::accept_T); + .def("accept_Tptr", &MyTemplatePoint2::accept_Tptr); + .def("create_MixedPtrs", &MyTemplatePoint2::create_MixedPtrs); + .def("create_ptrs", &MyTemplatePoint2::create_ptrs); + .def("return_T", &MyTemplatePoint2::return_T); + .def("return_Tptr", &MyTemplatePoint2::return_Tptr); + .def("return_ptrs", &MyTemplatePoint2::return_ptrs); + .def("templatedMethod", &MyTemplatePoint2::templatedMethod); + .def("templatedMethod", &MyTemplatePoint2::templatedMethod); +; + +class_("MyTemplatePoint3") + .def("MyTemplatePoint3", &MyTemplatePoint3::MyTemplatePoint3); + .def("accept_T", &MyTemplatePoint3::accept_T); + .def("accept_Tptr", &MyTemplatePoint3::accept_Tptr); + .def("create_MixedPtrs", &MyTemplatePoint3::create_MixedPtrs); + .def("create_ptrs", &MyTemplatePoint3::create_ptrs); + .def("return_T", &MyTemplatePoint3::return_T); + .def("return_Tptr", &MyTemplatePoint3::return_Tptr); + .def("return_ptrs", &MyTemplatePoint3::return_ptrs); + .def("templatedMethod", &MyTemplatePoint3::templatedMethod); + .def("templatedMethod", &MyTemplatePoint3::templatedMethod); +; + +class_("MyFactorPosePoint2") + .def("MyFactorPosePoint2", &MyFactorPosePoint2::MyFactorPosePoint2); +; + +def("aGlobalFunction", aGlobalFunction); +def("overloadedGlobalFunction", overloadedGlobalFunction); +}