Skip to content

Commit

Permalink
Upload to github
Browse files Browse the repository at this point in the history
  • Loading branch information
ojwoodford committed Feb 15, 2023
0 parents commit e1cf778
Show file tree
Hide file tree
Showing 8 changed files with 302 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.asv
*~
*.mex*
.DS_Store
53 changes: 53 additions & 0 deletions class_handle.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#ifndef __CLASS_HANDLE_HPP__
#define __CLASS_HANDLE_HPP__
#include "mex.h"
#include <stdint.h>
#include <string>
#include <cstring>
#include <typeinfo>

#define CLASS_HANDLE_SIGNATURE 0xFF00F0A5
template<class base> class class_handle
{
public:
class_handle(base *ptr) : signature_m(CLASS_HANDLE_SIGNATURE), name_m(typeid(base).name()), ptr_m(ptr) {}
~class_handle() { signature_m = 0; delete ptr_m; }
bool isValid() { return ((signature_m == CLASS_HANDLE_SIGNATURE) && !strcmp(name_m.c_str(), typeid(base).name())); }
base* ptr() { return ptr_m; }

private:
uint32_t signature_m;
const std::string name_m;
base* const ptr_m;
};

template<class base> inline mxArray *convertPtr2Mat(base *ptr)
{
mexLock();
mxArray *out = mxCreateNumericMatrix(1, 1, mxUINT64_CLASS, mxREAL);
*((uint64_t *)mxGetData(out)) = reinterpret_cast<uint64_t>(new class_handle<base>(ptr));
return out;
}

template<class base> inline class_handle<base> *convertMat2HandlePtr(const mxArray *in)
{
if (mxGetNumberOfElements(in) != 1 || mxGetClassID(in) != mxUINT64_CLASS || mxIsComplex(in))
mexErrMsgTxt("Input must be a real uint64 scalar.");
class_handle<base> *ptr = reinterpret_cast<class_handle<base> *>(*((uint64_t *)mxGetData(in)));
if (!ptr->isValid())
mexErrMsgTxt("Handle not valid.");
return ptr;
}

template<class base> inline base *convertMat2Ptr(const mxArray *in)
{
return convertMat2HandlePtr<base>(in)->ptr();
}

template<class base> inline void destroyObject(const mxArray *in)
{
delete convertMat2HandlePtr<base>(in);
mexUnlock();
}

#endif // __CLASS_HANDLE_HPP__
27 changes: 27 additions & 0 deletions example_interface.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
%EXAMPLE_INTERFACE Example MATLAB class wrapper to an underlying C++ class
classdef example_interface < handle
properties (SetAccess = private, Hidden = true)
objectHandle; % Handle to the underlying C++ class instance
end
methods
%% Constructor - Create a new C++ class instance
function this = example_interface(varargin)
this.objectHandle = example_mex('new', varargin{:});
end

%% Destructor - Destroy the C++ class instance
function delete(this)
example_mex('delete', this.objectHandle);
end

%% Train - an example class method call
function varargout = train(this, varargin)
[varargout{1:nargout}] = example_mex('train', this.objectHandle, varargin{:});
end

%% Test - another example class method call
function varargout = test(this, varargin)
[varargout{1:nargout}] = example_mex('test', this.objectHandle, varargin{:});
end
end
end
71 changes: 71 additions & 0 deletions example_mex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#include "mex.h"
#include "class_handle.hpp"

// The class that we are interfacing to
class example
{
public:
example() { mexPrintf("Calling constructor\n"); }
~example() { mexPrintf("Calling destructor\n"); }
void train() { mexPrintf("Calling train\n"); };
void test() { mexPrintf("Calling test\n"); };
private:
};

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
// Get the command string
char cmd[64];
if (nrhs < 1 || mxGetString(prhs[0], cmd, sizeof(cmd)))
mexErrMsgTxt("First input should be a command string less than 64 characters long.");

// New
if (!strcmp("new", cmd)) {
// Check parameters
if (nlhs != 1)
mexErrMsgTxt("New: One output expected.");
// Return a handle to a new C++ instance
plhs[0] = convertPtr2Mat<example>(new example);
return;
}

// Check there is a second input, which should be the class instance handle
if (nrhs < 2)
mexErrMsgTxt("Second input should be a class instance handle.");

// Delete
if (!strcmp("delete", cmd)) {
// Destroy the C++ object
destroyObject<example>(prhs[1]);
// Warn if other commands were ignored
if (nlhs != 0 || nrhs != 2)
mexWarnMsgTxt("Delete: Unexpected arguments ignored.");
return;
}

// Get the class instance pointer from the second input
example* example_instance = convertMat2Ptr<example>(prhs[1]);

// Call the various class methods
// Train
if (!strcmp("train", cmd)) {
// Check parameters
if (nlhs < 0 || nrhs < 2)
mexErrMsgTxt("Train: Unexpected arguments.");
// Call the method
example_instance->train();
return;
}
// Test
if (!strcmp("test", cmd)) {
// Check parameters
if (nlhs < 0 || nrhs < 2)
mexErrMsgTxt("Test: Unexpected arguments.");
// Call the method
example_instance->test();
return;
}

// Got here, so command not recognized
mexErrMsgTxt("Command not recognized.");
}
24 changes: 24 additions & 0 deletions license.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Copyright (c) 2018, Oliver Woodford
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
49 changes: 49 additions & 0 deletions mex_interface.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
%MEX_INTERFACE MATLAB wrapper to an underlying C++ class
%
% This interface assumes that the mex function uses the following standard
% interface:
% Construction - obj = mexfun('new', ...)
% Destruction - mexfun('delete', obj)
% Other methods - [...] = mexfun('method', obj, ...)
classdef mex_interface < handle
properties (Access = private, Hidden = true)
objectHandle; % Handle to the underlying C++ class instance
mexHandle; % Handle to the mex function
end
methods
%% Constructor - Create a new C++ class instance
% Inputs:
% mexfun - handle to the C++ class interface mex.
% varargin - arguments passed to the mex when calling 'new'.
function this = mex_interface(mexfun, varargin)
this.mexHandle = mexfun;
this.objectHandle = this.mexHandle('new', varargin{:});
end

%% Destructor - Destroy the C++ class instance
function delete(this)
if ~isempty(this.objectHandle)
this.mexHandle('delete', this.objectHandle);
end
this.objectHandle = [];
end

%% Disp - get the function name
function disp(this, var_name)
if nargin > 1
fprintf('%s is an object instance of %s\n', var_name, func2str(this.mexHandle));
else
fprintf('Object instance of %s\n', func2str(this.mexHandle));
end
end

%% All other methods
function varargout = subsref(this, s)
if numel(s) < 2 || ~isequal(s(1).type, '.') || ~isequal(s(2).type, '()')
error('Not a valid indexing expression')
end
assert(~isempty(this.objectHandle), 'Object not initialized correctly');
[varargout{1:nargout}] = this.mexHandle(s(1).subs, this.objectHandle, s(2).subs{:});
end
end
end
33 changes: 33 additions & 0 deletions run_example.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
function run_example()
% Check if the mex exists
dir = fileparts(mfilename('fullpath'));
if ~isequal(fileparts(which('example_mex')), dir)
% Compile the mex
cwd = cd(dir);
cleanup_obj = onCleanup(@() cd(cwd));
fprintf('Compiling example_mex\n');
mex example_mex.cpp
end

% Use the example interface
% This is the interface written specifically for the example class
fprintf('Using the example interface\n');
obj = example_interface();
train(obj);
test(obj);
clear obj % Clear calls the delete method

% Use the standard interface
% This interface can be used for any mex interface function using the
% pattern:
% Construction - obj = mexfun('new', ...)
% Destruction - mexfun('delete', obj)
% Other methods - [...] = mexfun('method', obj, ...)
% The standard interface avoids the need to write a specific interface
% class for each mex file.
fprintf('Using the standard interface\n');
obj = mex_interface(str2fun([dir '/example_mex'])); % str2fun allows us to use the full path, so the mex need not be on our path
obj.train();
obj.test();
clear obj % Clear calls the delete method
end
41 changes: 41 additions & 0 deletions str2fun.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
%STR2FUN Construct a function_handle from a function name or path.
% FUNHANDLE = STR2FUN(S) constructs a function_handle FUNHANDLE to the
% function named in the character vector S. The S input must be a
% character vector. The S input cannot be a character array with
% multiple rows or a cell array of character vectors.
%
% You can create a function handle using either the @function syntax or
% the STR2FUN command. You can create an array of function handles from
% character vectors by creating the handles individually with STR2FUN,
% and then storing these handles in a cell array.
%
% Examples:
%
% To create a function handle from the function name, 'humps':
%
% fhandle = str2func('humps')
% fhandle =
% @humps
%
% To call STR2FUNC on a cell array of character vectors, use the
% CELLFUN function. This returns a cell array of function handles:
%
% fh_array = cellfun(@str2func, {'sin' 'cos' 'tan'}, ...
% 'UniformOutput', false);
% fh_array{2}(5)
% ans =
% 0.2837
%
% See also STR2FUNC, FUNCTION_HANDLE, FUNC2STR, FUNCTIONS.

function fun = str2fun(str)
assert(ischar(str));
if str(1) ~= '@'
[p, str] = fileparts(str);
if ~isempty(p)
cwd = cd(p);
cleanup_obj = onCleanup(@() cd(cwd));
end
end
fun = str2func(str);
end

0 comments on commit e1cf778

Please sign in to comment.