diff --git a/wrap/Class.cpp b/wrap/Class.cpp index f6763ac31..45c2268ed 100644 --- a/wrap/Class.cpp +++ b/wrap/Class.cpp @@ -35,16 +35,22 @@ void Class::matlab_proxy(const string& classFile) const { string matlabName = qualifiedName(); // emit class proxy code - file.oss << "classdef " << matlabName << endl; + // we want our class to inherit the handle class for memory purposes + file.oss << "classdef " << matlabName << " < handle" << endl; file.oss << " properties" << endl; file.oss << " self = 0" << endl; file.oss << " end" << endl; file.oss << " methods" << endl; + // constructor file.oss << " function obj = " << matlabName << "(varargin)" << endl; BOOST_FOREACH(Constructor c, constructors) c.matlab_proxy_fragment(file,matlabName); file.oss << " if nargin ~= 13 && obj.self == 0, error('" << matlabName << " constructor failed'); end" << endl; file.oss << " end" << endl; + // deconstructor + file.oss << " function delete(obj)" << endl; + file.oss << " delete_" << matlabName << "(obj);" << endl; + file.oss << " end" << endl; file.oss << " function display(obj), obj.print(''); end" << endl; file.oss << " function disp(obj), obj.display; end" << endl; file.oss << " end" << endl; @@ -62,6 +68,11 @@ void Class::matlab_constructors(const string& toolboxPath, const vector& } } +/* ************************************************************************* */ +void Class::matlab_deconstructor(const string& toolboxPath, const vector& using_namespaces) const { + d.matlab_mfile (toolboxPath, qualifiedName()); + d.matlab_wrapper(toolboxPath, qualifiedName("::"), qualifiedName(), using_namespaces, includes); +} /* ************************************************************************* */ void Class::matlab_methods(const string& classPath, const vector& using_namespaces) const { string matlabName = qualifiedName(), cppName = qualifiedName("::"); @@ -88,6 +99,7 @@ void Class::matlab_make_fragment(FileWriter& file, string matlabClassName = qualifiedName(); BOOST_FOREACH(Constructor c, constructors) file.oss << mex << c.matlab_wrapper_name(matlabClassName) << ".cpp" << endl; + file.oss << mex << d.matlab_wrapper_name(matlabClassName) << ".cpp" << endl; BOOST_FOREACH(StaticMethod sm, static_methods) file.oss << mex << matlabClassName + "_" + sm.name << ".cpp" << endl; file.oss << endl << "cd @" << matlabClassName << endl; @@ -119,6 +131,7 @@ void Class::makefile_fragment(FileWriter& file) const { string file_base = c.matlab_wrapper_name(matlabName); file_names.push_back(file_base); } + file_names.push_back(d.matlab_wrapper_name(matlabName)); BOOST_FOREACH(StaticMethod c, static_methods) { string file_base = matlabName + "_" + c.name; file_names.push_back(file_base); diff --git a/wrap/Class.h b/wrap/Class.h index 235aa4ba4..1fec927e8 100644 --- a/wrap/Class.h +++ b/wrap/Class.h @@ -20,6 +20,7 @@ #include #include "Constructor.h" +#include "Deconstructor.h" #include "Method.h" #include "StaticMethod.h" @@ -37,12 +38,15 @@ struct Class { std::vector static_methods; ///< Static methods std::vector namespaces; ///< Stack of namespaces std::vector includes; ///< header include overrides + Deconstructor d; bool verbose_; ///< verbose flag // And finally MATLAB code is emitted, methods below called by Module::matlab_code void matlab_proxy(const std::string& classFile) const; ///< emit proxy class void matlab_constructors(const std::string& toolboxPath, const std::vector& using_namespaces) const; ///< emit constructor wrappers + void matlab_deconstructor(const std::string& toolboxPath, + const std::vector& using_namespaces) const; void matlab_methods(const std::string& classPath, const std::vector& using_namespaces) const; ///< emit method wrappers void matlab_static_methods(const std::string& classPath, diff --git a/wrap/Deconstructor.cpp b/wrap/Deconstructor.cpp new file mode 100644 index 000000000..93ed724f5 --- /dev/null +++ b/wrap/Deconstructor.cpp @@ -0,0 +1,83 @@ +/* ---------------------------------------------------------------------------- + + * 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 Deconstructor.ccp + * @author Frank Dellaert + * @author Andrew Melim + **/ + +#include +#include + +#include + +#include "utilities.h" +#include "Deconstructor.h" + +using namespace std; +using namespace wrap; + +/* ************************************************************************* */ +string Deconstructor::matlab_wrapper_name(const string& className) const { + string str = "delete_" + className; + return str; +} + +/* ************************************************************************* */ +void Deconstructor::matlab_mfile(const string& toolboxPath, const string& qualifiedMatlabName) const { + + string matlabName = matlab_wrapper_name(qualifiedMatlabName); + + // open destination m-file + string wrapperFile = toolboxPath + "/" + matlabName + ".m"; + FileWriter file(wrapperFile, verbose_, "%"); + + // generate code + file.oss << "function result = " << matlabName << "(obj"; + if (args.size()) file.oss << "," << args.names(); + file.oss << ")" << endl; + file.oss << " error('need to compile " << matlabName << ".cpp');" << endl; + file.oss << "end" << endl; + + // close file + file.emit(true); +} + +/* ************************************************************************* */ +void Deconstructor::matlab_wrapper(const string& toolboxPath, + const string& cppClassName, + const string& matlabClassName, + const vector& using_namespaces, const vector& includes) const { + string matlabName = matlab_wrapper_name(matlabClassName); + + // open destination wrapperFile + string wrapperFile = toolboxPath + "/" + matlabName + ".cpp"; + FileWriter file(wrapperFile, verbose_, "//"); + + // generate code + // + generateIncludes(file, name, includes); + cout << "Generate includes " << name << endl; + generateUsingNamespace(file, using_namespaces); + + file.oss << "void mexFunction(int nargout, mxArray *out[], int nargin, const mxArray *in[])" << endl; + file.oss << "{" << endl; + //Deconstructor takes 1 arg, the mxArray obj + file.oss << " checkArguments(\"" << matlabName << "\",nargout,nargin," << "1" << ");" << endl; + file.oss << " delete_shared_ptr< " << cppClassName << " >(in[0],\"" << matlabClassName << "\");" << endl; + file.oss << "}" << endl; + + // close file + file.emit(true); +} + +/* ************************************************************************* */ diff --git a/wrap/Deconstructor.h b/wrap/Deconstructor.h new file mode 100644 index 000000000..a5af4e327 --- /dev/null +++ b/wrap/Deconstructor.h @@ -0,0 +1,61 @@ +/* ---------------------------------------------------------------------------- + + * 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 Deconstructor.h + * @brief class describing a constructor + code generation + * @author Frank Dellaert + * @author Andrew Melim + **/ + +#pragma once + +#include +#include + +#include "Argument.h" + +namespace wrap { + +// Deconstructor class +struct Deconstructor { + + /// Deconstructor creates an empty class + Deconstructor(bool verbose = true) : + verbose_(verbose) { + } + + // Then the instance variables are set directly by the Module deconstructor + ArgumentList args; + std::string name; + bool verbose_; + + // MATLAB code generation + // toolboxPath is main toolbox directory, e.g., ../matlab + // classFile is class proxy file, e.g., ../matlab/@Point2/Point2.m + + /// wrapper name + std::string matlab_wrapper_name(const std::string& className) const; + + /// m-file + void matlab_mfile(const std::string& toolboxPath, + const std::string& qualifiedMatlabName) const; + + /// cpp wrapper + void matlab_wrapper(const std::string& toolboxPath, + const std::string& cppClassName, + const std::string& matlabClassName, + const std::vector& using_namespaces, + const std::vector& includes) const; +}; + +} // \namespace wrap + diff --git a/wrap/Module.cpp b/wrap/Module.cpp index efaac5be4..e4321dd66 100644 --- a/wrap/Module.cpp +++ b/wrap/Module.cpp @@ -12,6 +12,8 @@ /** * @file Module.ccp * @author Frank Dellaert + * @author Alex Cunningham + * @author Andrew Melim **/ #include "Module.h" @@ -49,6 +51,7 @@ Module::Module(const string& interfacePath, Argument arg0, arg; ArgumentList args0, args; Constructor constructor0(enable_verbose), constructor(enable_verbose); + Deconstructor deconstructor0(enable_verbose), deconstructor(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); @@ -180,7 +183,10 @@ Module::Module(const string& interfacePath, >> str_p("};")) [assign_a(cls.namespaces, namespaces)] [append_a(cls.includes, namespace_includes)] + [assign_a(deconstructor.name,cls.name)] + [assign_a(cls.d, deconstructor)] [push_back_a(classes,cls)] + [assign_a(deconstructor,deconstructor0)] [assign_a(cls,cls0)]; Rule namespace_def_p = @@ -339,6 +345,9 @@ void Module::matlab_code(const string& toolboxPath, cls.matlab_static_methods(toolboxPath,using_namespaces); cls.matlab_methods(classPath,using_namespaces); + // create deconstructor + cls.matlab_deconstructor(toolboxPath,using_namespaces); + // add lines to make m-file makeModuleMfile.oss << "%% " << cls.qualifiedName() << endl; makeModuleMfile.oss << "cd(toolboxpath)" << endl; diff --git a/wrap/matlab.h b/wrap/matlab.h index 631abb659..57d115c6b 100644 --- a/wrap/matlab.h +++ b/wrap/matlab.h @@ -281,8 +281,6 @@ gtsam::Matrix unwrap< gtsam::Matrix >(const mxArray* array) { // inspired by mexhandle, but using shared_ptr //***************************************************************************** -template class Collector; - template class ObjectHandle { private: @@ -296,13 +294,13 @@ public: ObjectHandle(T* ptr) : type(&typeid(T)), t(shared_ptr (ptr)) { signature = this; - Collector::register_handle(this); + this->print("Constructor"); } // Constructor for shared pointers // Creates shared pointer, will delete if is last one to hold pointer ObjectHandle(shared_ptr ptr) : - type(&typeid(T)), t(ptr) { + /*type(&typeid(T)),*/ t(ptr) { signature = this; } @@ -364,42 +362,6 @@ public: return obj; } - friend class Collector ; // allow Collector access to signature -}; - -// --------------------------------------------------------- -// ------------------ Garbage Collection ------------------- -// --------------------------------------------------------- - -// Garbage collection singleton (one collector object for each type T). -// Ensures that registered handles are deleted when the dll is released (they -// may also be deleted previously without problem). -// The Collector provides protection against resource leaks in the case -// where 'clear all' is called in MatLab. (This is because MatLab will call -// the destructors of statically allocated objects but not free-store allocated -// objects.) -template -class Collector { - typedef ObjectHandle Handle; - typedef std::list< Handle* > ObjList; - typedef typename ObjList::iterator iterator; - ObjList objlist; -public: - ~Collector() { - for (iterator i= objlist.begin(); i!=objlist.end(); ++i) { - if ((*i)->signature == *i) // check for valid signature - delete *i; - } - } - - static void register_handle (Handle* obj) { - static Collector singleton; - singleton.objlist.push_back(obj); - } - -private: // prevent construction - Collector() {} - Collector(const Collector&); }; //***************************************************************************** @@ -462,6 +424,7 @@ mxArray* wrap_shared_ptr(shared_ptr< Class > shared_ptr, const char *classname) */ template shared_ptr unwrap_shared_ptr(const mxArray* obj, const string& className) { + //Why is this here? #ifndef UNSAFE_WRAP bool isClass = mxIsClass(obj, className.c_str()); if (!isClass) { @@ -475,4 +438,20 @@ shared_ptr unwrap_shared_ptr(const mxArray* obj, const string& className) return handle->get_object(); } +template +void delete_shared_ptr(const mxArray* obj, const string& className) { + //Why is this here? +#ifndef UNSAFE_WRAP + bool isClass = true;//mxIsClass(obj, className.c_str()); + if (!isClass) { + mexPrintf("Expected %s, got %s\n", className.c_str(), mxGetClassName(obj)); + error("Argument has wrong type."); + } +#endif + mxArray* mxh = mxGetProperty(obj,0,"self"); + if (mxh==NULL) error("unwrap_reference: invalid wrap object"); + ObjectHandle* handle = ObjectHandle::from_mex_handle(mxh); + delete handle; +} + //***************************************************************************** diff --git a/wrap/tests/testMemory.m b/wrap/tests/testMemory.m new file mode 100644 index 000000000..ec3aad786 --- /dev/null +++ b/wrap/tests/testMemory.m @@ -0,0 +1,7 @@ +%MATLAB testing file for memory allocation and leaks +%Andrew Melim + +addpath([pwd,'/../../../toolbox/gtsam']); +for i=1:10 + p = gtsamPoint2() +end