Memory leak fixed within wrap. Must call 'clear' before 'clear all' and 'clear classes'. Wrap now creates delete_classname files for all wrapped classes. MATLAB classes now derive from the handle superclass.

release/4.3a0
Andrew Melim 2012-03-30 05:34:23 +00:00
parent cdbeebaf1d
commit a1da906d33
7 changed files with 197 additions and 41 deletions

View File

@ -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<string>&
}
}
/* ************************************************************************* */
void Class::matlab_deconstructor(const string& toolboxPath, const vector<string>& 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<string>& 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);

View File

@ -20,6 +20,7 @@
#include <string>
#include "Constructor.h"
#include "Deconstructor.h"
#include "Method.h"
#include "StaticMethod.h"
@ -37,12 +38,15 @@ struct Class {
std::vector<StaticMethod> static_methods; ///< Static methods
std::vector<std::string> namespaces; ///< Stack of namespaces
std::vector<std::string> 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<std::string>& using_namespaces) const; ///< emit constructor wrappers
void matlab_deconstructor(const std::string& toolboxPath,
const std::vector<std::string>& using_namespaces) const;
void matlab_methods(const std::string& classPath,
const std::vector<std::string>& using_namespaces) const; ///< emit method wrappers
void matlab_static_methods(const std::string& classPath,

83
wrap/Deconstructor.cpp Normal file
View File

@ -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 <iostream>
#include <fstream>
#include <boost/foreach.hpp>
#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<string>& using_namespaces, const vector<string>& 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);
}
/* ************************************************************************* */

61
wrap/Deconstructor.h Normal file
View File

@ -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 <string>
#include <vector>
#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<std::string>& using_namespaces,
const std::vector<std::string>& includes) const;
};
} // \namespace wrap

View File

@ -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;

View File

@ -281,8 +281,6 @@ gtsam::Matrix unwrap< gtsam::Matrix >(const mxArray* array) {
// inspired by mexhandle, but using shared_ptr
//*****************************************************************************
template<typename T> class Collector;
template<typename T>
class ObjectHandle {
private:
@ -296,13 +294,13 @@ public:
ObjectHandle(T* ptr) :
type(&typeid(T)), t(shared_ptr<T> (ptr)) {
signature = this;
Collector<T>::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<T> ptr) :
type(&typeid(T)), t(ptr) {
/*type(&typeid(T)),*/ t(ptr) {
signature = this;
}
@ -364,42 +362,6 @@ public:
return obj;
}
friend class Collector<T> ; // 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 <typename T>
class Collector {
typedef ObjectHandle<T> 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 <typename Class>
shared_ptr<Class> 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<Class> unwrap_shared_ptr(const mxArray* obj, const string& className)
return handle->get_object();
}
template <typename Class>
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<Class>* handle = ObjectHandle<Class>::from_mex_handle(mxh);
delete handle;
}
//*****************************************************************************

7
wrap/tests/testMemory.m Normal file
View File

@ -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