├── .gitignore ├── LICENSE ├── MinGW ├── README.md ├── mex_C++_mingw-w64.xml ├── mingw-w64_vars.bat └── mingw_mexopts.bat ├── README.md ├── cppClass ├── class_wrapper_template.cpp ├── cppclass.m ├── pqheap.cpp ├── pqheap.hpp └── pqheap.m └── propertySheets ├── MATLAB.props ├── MATLABx64.props ├── MATLABx86.props └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | *.mexw64 3 | *.mexw32 4 | *.mexmaci64 5 | *.mexa64 6 | *.lib -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jonathan Chappelow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MinGW/README.md: -------------------------------------------------------------------------------- 1 | These files are used to help setup a MinGW distribution such as MinGW-w64 or TDM-GCC to compile MATLAB MEX files. For detailed instructions, see the following Stack Overflow answer: 2 | 3 | [Using GCC (MinGW) as MATLAB's MEX compiler](http://stackoverflow.com/a/28490382/2778484) 4 | -------------------------------------------------------------------------------- /MinGW/mex_C++_mingw-w64.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 24 |
43 | 48 | 49 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 101 | 107 | 108 | -------------------------------------------------------------------------------- /MinGW/mingw-w64_vars.bat: -------------------------------------------------------------------------------- 1 | rem @echo off 2 | set MW_TARGET_ARCH=win64 3 | set MINGWROOT=C:\mingw-w64\x86_64-4.9.2-release-posix-seh-rt_v3-rev1 4 | set PATH=%MINGWROOT%\bin;%PATH% 5 | -------------------------------------------------------------------------------- /MinGW/mingw_mexopts.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM MinGW-w64 C++ mexopts.bat 4 | REM by Jonathan Chappelow (chappjc) 5 | REM re: http://stackoverflow.com/a/28490382/2778484 6 | REM This is the legacy MEX configuration method. See the XML version at: 7 | REM https://github.com/chappjc/MATLAB/blob/master/MinGW/mex_C%2B%2B_mingw-w64.xml 8 | 9 | REM Edit MINGWROOT or set an environment variable 10 | set MINGWROOT=C:\ProgFiles\MinGW-w64\mingw64 11 | set PATH=%MINGWROOT%\bin;%PATH% 12 | 13 | set MATLAB=%MATLAB_ROOT% 14 | set MW_TARGET_ARCH=win64 15 | 16 | set COMPILER=x86_64-w64-mingw32-g++ 17 | set COMPFLAGS=-c -m64 -mwin32 -mdll -Wall -DMATLAB_MEX_FILE 18 | REM Optionally add -std=c++11 to COMPFLAGS (-std=c99 for C) 19 | set OPTIMFLAGS=-DNDEBUG -O3 20 | set DEBUGFLAGS=-g 21 | set NAME_OBJECT=-o 22 | 23 | set LIBLOC=%MATLAB%\extern\lib\%MW_TARGET_ARCH%\microsoft 24 | set LINKER=x86_64-w64-mingw32-g++ 25 | set LINKFLAGS=-shared -L"%LIBLOC%" -L"%MATLAB%\bin\win64" 26 | REM Optionally add to LINKFLAGS after -shared to get standalone output: -static-libstdc++ -static-libgcc 27 | set LINKFLAGSPOST=-lmx -lmex -leng -lmat -lmwlapack -lmwblas 28 | set LINKOPTIMFLAGS=-O3 29 | set LINKDEBUGFLAGS=-g 30 | set LINK_FILE= 31 | set LINK_LIB= 32 | set NAME_OUTPUT=-o "%OUTDIR%%MEX_NAME%%MEX_EXT%" 33 | 34 | set RC_COMPILER= 35 | set RC_LINKER= 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MATLAB 2 | ====== 3 | 4 | The repository contains a few small projects designed to help utilize C++ code in MATLAB. Currently there are some compiler configurations and a class wrapper: 5 | 1. **MinGW** contains configuration files to allow the use of MinGW's GCC compiler's to build MEX files. 6 | 2. **cppClass** contains an example of how to wrap a C++ class by MATLAB MEX file, with a bonus MATLAB class to manage the interface and lifetime of the underlying instances. 7 | 3. **propertySheets** contains Visual Studio property sheets for setting up projects to build MEX files from Visual Studio directly. 8 | 9 | See also [my GitHub Gists](https://gist.github.com/chappjc). 10 | -------------------------------------------------------------------------------- /cppClass/class_wrapper_template.cpp: -------------------------------------------------------------------------------- 1 | // WORK IN PROGRESS! 2 | // 3 | // class_wrapper_template.cpp 4 | // Example of using a C++ class via a MEX-file 5 | // by Jonathan Chappelow (chappjc) 6 | // 7 | // Design goals: 8 | // 1. Manage multiple persistent instances of a C++ class 9 | // 2. Small consecutive integer handles used in MATLAB (not cast pointers) 10 | // 3. Transparently handle resource management (i.e. MATLAB never 11 | // responsible for memory allocated for C++ classes) 12 | // a. No memory leaked if MATLAB fails to issue "delete" action 13 | // b. Automatic deallocation if MEX-file prematurely unloaded 14 | // 4. Guard against premature module unloading 15 | // 5. Validity of handles implicitly verified without checking a magic number 16 | // 6. No wrapper class or functions mimicking mexFunction, just an intuitive 17 | // switch-case block in mexFunction. 18 | // 19 | // Note that these goals should be acheved without regard to any MATLAB class, 20 | // but which can also help address memory management issues. As such, the 21 | // resulting MEX-file can safely be used directly (but not too elegantly). 22 | // 23 | // Use: 24 | // 1. Enumerate the different actions (e.g. New, Delete, Insert, etc.) in the 25 | // Actions enum. For each enumerated action, specify a string (e.g. 26 | // "new", "delete", "insert", etc.) to be passed as the first argument to 27 | // the MEX function in MATLAB. 28 | // 2. Customize the handling for each action in the switch statement in the 29 | // body of mexFunction (e.g. call the relevant C++ class method). 30 | // 31 | // Implementation: 32 | // 33 | // For your C++ class, class_type, mexFunction uses static data storage to hold 34 | // a persistent (between calls to mexFunction) table of integer handles and 35 | // smart pointers to dynamically allocated class instances. A std::map is used 36 | // for this purpose, which facilitates locating known handles, for which only 37 | // valid instances of your class are guaranteed to exist: 38 | // 39 | // typedef unsigned int handle_type; 40 | // std::map> 41 | // 42 | // A std::shared_ptr takes care of deallocation when either (1) a table element 43 | // is erased via the "delete" action or (2) the MEX-file is unloaded. 44 | // 45 | // To prevent the MEX-file from unloading while a MATLAB class instances exist, 46 | // mexLock is called each time a new C++ class instance is created, adding to 47 | // the MEX-file's lock count. Each time a C++ instance is deleted mexUnlock is 48 | // called, removing one lock from the lock count. 49 | // 50 | // Requirements: 51 | // 52 | // A modern compiler with the following C++11 features: 53 | // - shared_ptr 54 | // - auto 55 | // - enum class 56 | // - initializer_list (for const map initialization) 57 | // (VS2013, recent GCC possibly with -std=c++11, Clang since 3.1) 58 | // 59 | // TODO: 60 | // 61 | // - This example uses a priority queue class of mine, which is far from the 62 | // simplest example. Demonstrate with something more basic. 63 | // - Somehow put the relevant parts in a header, OR the other way around -- put 64 | // user class config in a header and include into the mexFunction's cpp. 65 | // 66 | // 04/25/15 (chappjc) - Initial version. 67 | 68 | #include "mex.h" 69 | 70 | #include 71 | #include 72 | #include 73 | #include 74 | #include 75 | #include 76 | 77 | //////////////////////// BEGIN Step 1: Configuration //////////////////////// 78 | 79 | // Include your class declaration 80 | #include "pqheap.hpp" 81 | 82 | // Define class_type for your class 83 | typedef double data_type; 84 | typedef PQheap class_type; 85 | // typedef MyClass class_type; // basic case 86 | 87 | // List actions 88 | enum class Action 89 | { 90 | // create/destroy instance - REQUIRED 91 | New, 92 | Delete, 93 | // user-specified class functionality 94 | Init, 95 | Insert, 96 | ExtractTop, 97 | Size, 98 | Capacity, 99 | Print 100 | }; 101 | 102 | // Map string (first input argument to mexFunction) to an Action 103 | const std::map actionTypeMap = 104 | { 105 | { "new", Action::New }, 106 | { "delete", Action::Delete }, 107 | { "init", Action::Init }, 108 | { "insert", Action::Insert }, 109 | { "extract", Action::ExtractTop }, 110 | { "size", Action::Size }, 111 | { "capacity", Action::Capacity }, 112 | { "print", Action::Print } 113 | }; // if no initializer list available, put declaration and inserts into mexFunction 114 | 115 | ///////////////////////// END Step 1: Configuration ///////////////////////// 116 | 117 | typedef unsigned int handle_type; 118 | typedef std::pair> indPtrPair_type; // or boost::shared_ptr 119 | typedef std::map instanceMap_type; 120 | typedef indPtrPair_type::second_type instPtr_t; 121 | 122 | // getHandle pulls the integer handle out of prhs[1] 123 | handle_type getHandle(int nrhs, const mxArray *prhs[]); 124 | // checkHandle gets the position in the instance table 125 | instanceMap_type::const_iterator checkHandle(const instanceMap_type&, handle_type); 126 | 127 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { 128 | 129 | // static storage duration object for table mapping handles to instances 130 | static instanceMap_type instanceTab; 131 | 132 | if (nrhs < 1 || !mxIsChar(prhs[0])) 133 | mexErrMsgTxt("First input must be an action string ('new', 'delete', or a method name)."); 134 | 135 | char *actionCstr = mxArrayToString(prhs[0]); // convert char16_t to char 136 | std::string actionStr(actionCstr); mxFree(actionCstr); 137 | 138 | for (auto & c : actionStr) c = ::tolower(c); // remove this for case sensitivity 139 | 140 | if (actionTypeMap.count(actionStr) == 0) 141 | mexErrMsgTxt(("Unrecognized action (not in actionTypeMap): " + actionStr).c_str()); 142 | 143 | // If action is not "new" or "delete" try to locate an existing instance based on input handle 144 | instPtr_t instance; 145 | if (actionTypeMap.at(actionStr) != Action::New && actionTypeMap.at(actionStr) != Action::Delete) { 146 | handle_type h = getHandle(nrhs, prhs); 147 | instanceMap_type::const_iterator instIt = checkHandle(instanceTab, h); 148 | instance = instIt->second; 149 | } 150 | 151 | //////// Step 2: customize the each action in the switch in mexFuction //////// 152 | switch (actionTypeMap.at(actionStr)) 153 | { 154 | case Action::New: 155 | { 156 | if (nrhs > 1 && mxGetNumberOfElements(prhs[1]) != 1) 157 | mexErrMsgTxt("Second argument (optional) must be a scalar, N."); 158 | 159 | handle_type newHandle = instanceTab.size() ? (instanceTab.rbegin())->first + 1 : 1; 160 | 161 | std::pair insResult; 162 | if (nrhs > 1) { 163 | unsigned int N = static_cast(mxGetScalar(prhs[1])); 164 | insResult = instanceTab.insert(indPtrPair_type(newHandle, std::make_shared(N))); 165 | } 166 | else 167 | insResult = instanceTab.insert(indPtrPair_type(newHandle, std::make_shared())); // default constructor 168 | 169 | if (!insResult.second) // sanity check 170 | mexPrintf("Oh, bad news. Tried to add an existing handle."); // shouldn't ever happen 171 | else 172 | mexLock(); // add to the lock count 173 | 174 | // return the handle 175 | plhs[0] = mxCreateDoubleScalar(insResult.first->first); // == newHandle 176 | 177 | break; 178 | } 179 | case Action::Delete: 180 | { 181 | instanceMap_type::const_iterator instIt = checkHandle(instanceTab, getHandle(nrhs, prhs)); 182 | instanceTab.erase(instIt); 183 | mexUnlock(); 184 | plhs[0] = mxCreateLogicalScalar(instanceTab.empty()); // info 185 | break; 186 | } 187 | case Action::Init: 188 | if (nrhs < 3 || mxGetNumberOfElements(prhs[2]) != 1) 189 | mexErrMsgTxt("Max heap size must be a scalar."); 190 | 191 | plhs[0] = mxCreateDoubleScalar( 192 | instance->init(static_cast(mxGetScalar(prhs[2])))); 193 | 194 | break; 195 | 196 | case Action::Insert: 197 | { 198 | if (nrhs < 3 || mxGetNumberOfElements(prhs[2]) != 1) 199 | mexErrMsgTxt("Inserted element must be a scalar."); 200 | 201 | int heapPos = instance->insert(static_cast(mxGetScalar(prhs[2]))); 202 | plhs[0] = mxCreateDoubleScalar(heapPos); 203 | 204 | break; 205 | } 206 | case Action::Capacity: 207 | plhs[0] = mxCreateDoubleScalar(instance->capacity()); 208 | break; 209 | 210 | case Action::Size: 211 | plhs[0] = mxCreateDoubleScalar(instance->size()); 212 | break; 213 | 214 | case Action::ExtractTop: 215 | plhs[0] = mxCreateDoubleScalar( instance->size() > 0 ? 216 | instance->extractTop() : mxGetNaN() ); 217 | break; 218 | 219 | case Action::Print: 220 | { 221 | data_type const *data = instance->data(); 222 | const int len = instance->size(); 223 | for (int j = 1; j <= len; ++j) 224 | mexPrintf("| %g ", data[j]); 225 | mexPrintf("|\n"); 226 | break; 227 | } 228 | default: 229 | mexErrMsgTxt(("Unhandled action: " + actionStr).c_str()); 230 | break; 231 | } 232 | //////////////////////////////// DONE! //////////////////////////////// 233 | } 234 | 235 | handle_type getHandle(int nrhs, const mxArray *prhs[]) 236 | { 237 | if (nrhs < 2 || mxGetNumberOfElements(prhs[1]) != 1) // mxIsScalar in R2015a+ 238 | mexErrMsgTxt("Specify an instance with an integer handle."); 239 | return static_cast(mxGetScalar(prhs[1])); 240 | } 241 | 242 | instanceMap_type::const_iterator checkHandle(const instanceMap_type& m, handle_type h) 243 | { 244 | auto it = m.find(h); 245 | 246 | if (it == m.end()) { 247 | std::stringstream ss; ss << "No instance corresponding to handle " << h << " found."; 248 | mexErrMsgTxt(ss.str().c_str()); 249 | } 250 | 251 | return it; 252 | } 253 | -------------------------------------------------------------------------------- /cppClass/cppclass.m: -------------------------------------------------------------------------------- 1 | classdef (Abstract = true) cppclass < handle 2 | % cppclass: A base class for interfacing with a MEX file that wraps a C++ class 3 | % by Jonathan Chappelow (chappjc) 4 | % DO NOT EDIT. Derive from this class as demonstrated in: 5 | % https://github.com/chappjc/MATLAB/blob/master/cppClass/pqheap.m 6 | 7 | properties (GetAccess = private, SetAccess = immutable, Hidden = true, Transient = true) 8 | instanceHandle; % integer handle to a class instance in MEX function 9 | end 10 | 11 | properties (GetAccess = protected, SetAccess = immutable, Hidden = false) 12 | mexClassWrapperFnc; % the MEX function owning the class instances 13 | end 14 | 15 | methods 16 | 17 | function obj = cppclass(mexBackendFnc,varargin) 18 | 19 | if (nargin<1), 20 | error('cppclass:invalidConstruction',... 21 | ['Must specify MEX-file backend (provides the actual class ' ... 22 | 'functionality by wrapping a C++ class)']); 23 | end 24 | 25 | obj.mexClassWrapperFnc = cppclass.checkMEXFnc(mexBackendFnc); 26 | 27 | % create a new C++ class instance for this MATLAB class instance 28 | obj.instanceHandle = obj.mexClassWrapperFnc('new',varargin{:}); 29 | 30 | end 31 | 32 | function delete(obj) 33 | 34 | if ~isempty(obj.instanceHandle) 35 | obj.mexClassWrapperFnc('delete', obj.instanceHandle); 36 | % obj.mexClassWrapperFnc = []; 37 | % obj.instanceHandle = []; 38 | end 39 | 40 | end 41 | 42 | end 43 | 44 | methods (Sealed = true) 45 | 46 | function varargout = cppmethod(obj, methodName, varargin) 47 | if isempty(obj.instanceHandle), 48 | error('cppclass:invalidHandle','No class handle'); end 49 | [varargout{1:nargout}] = obj.mexClassWrapperFnc(methodName, obj.instanceHandle, varargin{:}); 50 | end 51 | 52 | end 53 | 54 | methods (Static = true) 55 | 56 | function mexFnc = checkMEXFnc(mexFnc) 57 | % Input function_handle or name, return valid handle or error 58 | 59 | % accept string or function_handle 60 | if ischar(mexFnc), 61 | mexFnc = str2func(mexFnc); 62 | end 63 | 64 | % validate MEX-file function handle 65 | % http://stackoverflow.com/a/19307825/2778484 66 | if isa(mexFnc, 'function_handle'), 67 | funInfo = functions(mexFnc); 68 | if exist(funInfo.file,'file') ~= 3, % status 3 is MEX-file 69 | error('cppclass:invalidMEXFunction','Invalid MEX file: "%s".',funInfo.file); 70 | end 71 | else 72 | error('cppclass:invalidFunctionHandle','Invalid function handle.'); 73 | end 74 | end 75 | 76 | end 77 | 78 | end 79 | -------------------------------------------------------------------------------- /cppClass/pqheap.cpp: -------------------------------------------------------------------------------- 1 | // pqheap.hpp - The PQheap class implementation (included by header, do not 2 | // compilie directly). 3 | // by Jonathan Chappelow (chappjc) 4 | 5 | #include 6 | 7 | template 8 | PQheap::PQheap() 9 | : heap(NULL), N(0), cap(0) 10 | { 11 | } 12 | 13 | template 14 | PQheap::PQheap(unsigned int maxSize) 15 | : heap(NULL), N(0), cap(maxSize) 16 | { 17 | init(cap); 18 | } 19 | 20 | template 21 | int PQheap::init(unsigned int maxSize) 22 | { 23 | N = 0; 24 | cap = maxSize; 25 | 26 | if (maxSize == 0) 27 | return 0; 28 | 29 | if (heap) 30 | delete[] heap; 31 | 32 | heap = new T[cap + 1]; 33 | 34 | if (!heap) { 35 | cap = 0; 36 | return 1; 37 | } 38 | 39 | return 0; 40 | } 41 | 42 | template 43 | PQheap::~PQheap() 44 | { 45 | if (heap) delete[] heap; 46 | N = 0; 47 | cap = 0; 48 | } 49 | 50 | template 51 | int PQheap::insert(const T& t) 52 | { 53 | if (cap == 0) 54 | return -2; 55 | 56 | if (N == cap) { 57 | if (cmp(t, heap[1])) { 58 | // replace top and sift down 59 | heap[1] = t; 60 | return heapDownQuick(); 61 | } 62 | else { 63 | return -1; 64 | } 65 | } 66 | 67 | // append bottom and bubble up 68 | heap[++N] = t; 69 | 70 | return N==1 ? 1 : heapUp(); 71 | } 72 | 73 | template 74 | int PQheap::heapUp() 75 | { 76 | return heapUp(N); 77 | /*assert(N > 1); 78 | 79 | unsigned int i = N; 80 | unsigned int p = parent(i); 81 | 82 | while (i > 1 && heap[p] > heap[i]) { 83 | swap(heap[p], heap[i]); 84 | i = p; 85 | p = parent(i); 86 | } 87 | 88 | return i;*/ 89 | } 90 | 91 | template 92 | int PQheap::heapUp(int i) 93 | { 94 | assert(N > 1); 95 | 96 | T bubble = heap[i]; 97 | 98 | //unsigned int i = N; 99 | int p = parent(i); 100 | 101 | while (i > 1 && cmp(heap[p], bubble) /*heap[i]*/) { 102 | //swap(heap[p], heap[i]); 103 | heap[i] = heap[p]; 104 | i = p; 105 | p = parent(i); 106 | } 107 | 108 | heap[i] = bubble; 109 | 110 | return i; 111 | } 112 | 113 | template 114 | int PQheap::extractTop(T& t) 115 | { 116 | if (N < 1) return 1; 117 | 118 | t = heap[1]; 119 | 120 | if (N > 1) 121 | heap[1] = heap[N]; 122 | 123 | #ifdef _DEBUG 124 | heap[N--] = T(); 125 | #else 126 | --N; 127 | #endif 128 | 129 | return N <= 1 ? 0 : heapDownQuick(); 130 | } 131 | 132 | template 133 | T PQheap::peakTop() const 134 | { 135 | return N > 0 ? heap[1] : T(); 136 | } 137 | 138 | template 139 | int PQheap::peakTop(const T* t) const 140 | { 141 | if (N < 1) return 1; 142 | 143 | t = &heap[1]; 144 | 145 | return 0; 146 | } 147 | 148 | template 149 | void PQheap::discardTop() 150 | { 151 | if (N < 1) return; 152 | if (N > 1) { 153 | heap[1] = heap[N--]; 154 | heapDownQuick(); 155 | } 156 | else 157 | --N; 158 | } 159 | 160 | template 161 | T PQheap::extractTop() 162 | { 163 | T top; 164 | extractTop(top); 165 | return top; 166 | } 167 | 168 | //template 169 | //int PQheap::heapDown() 170 | //{ 171 | // //return heapDown(1); 172 | // assert(N > 1); 173 | // 174 | // int i = 1; 175 | // int left = 2; // left_child(1) 176 | // int right = 3; // right_child(1) 177 | // 178 | // if (right <= N && heap[left] > heap[right]) 179 | // left = right; 180 | // 181 | // while (left <= N && heap[i] > heap[left]) { 182 | // swap(heap[left], heap[i]); 183 | // 184 | // i = left; 185 | // left = left_child(i); 186 | // right = left + 1; 187 | // 188 | // if (right <= N && heap[left] > heap[right]) 189 | // left = right; 190 | // } 191 | // 192 | // return i; 193 | //} 194 | 195 | template 196 | int PQheap::heapDown() 197 | { 198 | //return heapDown(1); 199 | assert(N > 1); 200 | 201 | int i = 1; 202 | int next = 2; // left_child(1) 203 | 204 | T rock = heap[i]; 205 | 206 | if (next < N && cmp(heap[next] , heap[next+1])) 207 | ++next; // go right 208 | 209 | while (next <= N && cmp(rock , heap[next])) { 210 | heap[i] = heap[next]; 211 | 212 | i = next; 213 | next = left_child(i); 214 | 215 | if (next < N && cmp(heap[next] , heap[next + 1])) 216 | ++next; 217 | } 218 | 219 | heap[i] = rock; 220 | 221 | return i; 222 | } 223 | 224 | template 225 | int PQheap::heapDownQuick() 226 | { 227 | //return heapDown(1); 228 | assert(N > 1); 229 | 230 | int i = 1; 231 | int next = 2; // left_child(1) 232 | 233 | T rock = heap[i]; 234 | 235 | while (i <= N/2) { 236 | next = left_child(i); 237 | 238 | if (next < N && cmp(heap[next] , heap[next + 1])) 239 | ++next; 240 | if (!cmp(rock , heap[next])) 241 | break; 242 | 243 | heap[i] = heap[next]; 244 | i = next; 245 | } 246 | 247 | heap[i] = rock; 248 | 249 | return i; 250 | } 251 | 252 | 253 | template 254 | int PQheap::heapDown(int i) 255 | { 256 | assert(N > 1); 257 | 258 | int left = left_child(i); 259 | int right = right_child(i); 260 | 261 | if (right <= N && cmp(heap[left] , heap[right])) 262 | left = right; 263 | 264 | while (left <= N && cmp(heap[i] , heap[left])) { 265 | swap(heap[left], heap[i]); 266 | 267 | i = left; 268 | left = left_child(i); 269 | right = left + 1; 270 | 271 | if (right <= N && cmp(heap[left] , heap[right])) 272 | left = right; 273 | } 274 | 275 | return i; 276 | } 277 | 278 | template 279 | int PQheap::heapify() 280 | { 281 | return heapDown(); 282 | } 283 | 284 | #if _MSC_VER < 1800 285 | template 286 | void PQheap::swap(T& a, T& b) { 287 | T tmp = a; 288 | a = b; 289 | b = tmp; 290 | } 291 | #else 292 | // #if __cplusplus >= 201103L 293 | #define XSTR(x) STR(x) 294 | #define STR(x) #x 295 | #pragma message("C++: " XSTR(__cplusplus) ", MSVC: " XSTR(_MSC_VER)) 296 | template 297 | void PQheap::swap(T& a, T& b) { 298 | T tmp(std::move(a)); 299 | a = std::move(b); 300 | b = std::move(tmp); 301 | } 302 | #endif -------------------------------------------------------------------------------- /cppClass/pqheap.hpp: -------------------------------------------------------------------------------- 1 | // pqheap.hpp 2 | // 3 | // Template parameters: 4 | // 1. typename T - the data type of each element. Can be any type for which 5 | // the comparator specified in the next parameter is defined. 6 | // 2. typename C - A functor: std::less or std::greater. To create a 7 | // min-oriented queue (top is smallest in N largest elements), use 8 | // std::greater. To create a max-oriented queue (top is largest in N 9 | // smallest elements), use std::less. 10 | // 11 | // by Jonathan Chappelow (chappjc) 12 | 13 | #ifndef PQHEAP_H_ 14 | #define PQHEAP_H_ 15 | 16 | #include 17 | 18 | //template 19 | template > 20 | class PQheap 21 | { 22 | public: 23 | 24 | PQheap(); 25 | ~PQheap(); 26 | 27 | int init(unsigned int maxSize); 28 | PQheap(unsigned int maxSize); 29 | 30 | typedef typename T element_type; 31 | 32 | public: 33 | 34 | int insert(const T& t); 35 | 36 | int extractTop(T& t); 37 | T extractTop(); 38 | 39 | int peakTop(const T* t) const; 40 | T peakTop() const; 41 | void discardTop(); 42 | int size() const { return N; } 43 | int capacity() const { return cap; } 44 | bool empty() const { return N == 0; } 45 | 46 | T const* data() { return heap; } 47 | 48 | public: 49 | 50 | static int left_child(int index) { return /*index << 1*/ 2 * index; } 51 | static int right_child(int index) { return /*(index << 1) + 1*/ 2 * index + 1; } 52 | static int parent(int index) { return index/2 /*index >> 1*/; } 53 | 54 | private: 55 | 56 | int heapUp(); 57 | int heapDown(); 58 | int heapDownQuick(); 59 | 60 | // duplicate code for speed, no default arguments 61 | int heapUp(int i); 62 | int heapDown(int i); // no default, use overload 63 | 64 | int heapify(); 65 | 66 | static void swap(T& a, T& b); 67 | 68 | private: 69 | 70 | T* heap; 71 | 72 | int N; 73 | int cap; 74 | C cmp; 75 | }; 76 | 77 | // actual implementation separate to keep this file short 78 | #include "pqheap.cpp" 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /cppClass/pqheap.m: -------------------------------------------------------------------------------- 1 | classdef pqheap < cppclass 2 | % Example class for how to derive from cppclass and interface with your C++ 3 | % class. Specifically, this demonstrates the use of a C++ priority queue. 4 | % by Jonathan Chappelow (chappjc) 5 | methods 6 | 7 | % Use the name of your MEX file here 8 | function obj = pqheap(varargin) 9 | obj@cppclass('class_wrapper_template',varargin{:}); 10 | end 11 | 12 | % new and delete are inherited, everything else calls cppmethod() 13 | 14 | % currentNumberOfElements = p.len() 15 | function varargout = len(obj) 16 | [varargout{1:nargout}] = obj.cppmethod('size'); 17 | end 18 | 19 | % maxSize = p.capacity() 20 | function varargout = capacity(obj) 21 | [varargout{1:nargout}] = obj.cppmethod('capacity'); 22 | end 23 | 24 | % position = p.insert(element) 25 | function varargout = insert(obj,element) 26 | if (obj.capacity()<1), 27 | error('pqheap:zeroCapacity','Zero capacity heap.'); end 28 | [varargout{1:nargout}] = obj.cppmethod('insert',element); 29 | end 30 | 31 | function fastInsert(obj,element) 32 | obj.cppmethod('insert',element); 33 | end 34 | 35 | % topElement = p.extract() 36 | function varargout = extract(obj) 37 | if (obj.len()<1), 38 | error('pqheap:heapEmpty','Heap is empty'); end 39 | [varargout{1:nargout}] = obj.cppmethod('extract'); 40 | end 41 | 42 | % p.printHeap() 43 | function printHeap(obj) 44 | obj.cppmethod('print'); 45 | end 46 | 47 | % init/reinit 48 | function varargout = init(obj,N) 49 | if (obj.len()>0), 50 | warning('pqheap:heapNotEmpty','Re-initializing non-empty heap'); end 51 | [varargout{1:nargout}] = obj.cppmethod('init',N); 52 | end 53 | 54 | end 55 | 56 | end 57 | -------------------------------------------------------------------------------- /propertySheets/MATLAB.props: -------------------------------------------------------------------------------- 1 |  2 | 5 | 6 | 7 | 8 | 9 | $(MATLAB32_ROOT) 10 | $(MATLAB_ROOT) 11 | $(MATLAB32_ROOT)\extern\lib\win32\microsoft 12 | $(MATLAB_ROOT)\extern\lib\win64\microsoft 13 | 14 | 15 | .mexw32 16 | .mexw64 17 | false 18 | DynamicLibrary 19 | gpu.lib 20 | 21 | 22 | 23 | 24 | 25 | $(MATLAB_BASE)\extern\include;$(MATLAB_BASE)\toolbox\distcomp\gpu\extern\include;%(AdditionalIncludeDirectories) 26 | MATLAB_MEX_FILE;%(PreprocessorDefinitions) 27 | 28 | 29 | $(MATLAB_LIB_PATH);%(AdditionalLibraryDirectories) 30 | libut.lib;libmx.lib;libmex.lib;libmat.lib;$(GPUArrayLib);%(AdditionalDependencies) 31 | true 32 | true 33 | /EXPORT:mexFunction %(AdditionalOptions) 34 | 35 | 36 | $(IntDir)$(TargetName)$(TargetExt).manifest 37 | 38 | 39 | $(IntDir)$(TargetName)$(TargetExt).manifest.res 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /propertySheets/MATLABx64.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | .mexw64 7 | false 8 | 9 | 10 | 11 | $(MATLAB_ROOT)\extern\include;%(AdditionalIncludeDirectories) 12 | MATLAB_MEX_FILE;%(PreprocessorDefinitions) 13 | 14 | 15 | $(MATLAB_ROOT)\extern\lib\win64\microsoft;%(AdditionalLibraryDirectories) 16 | libut.lib;libmx.lib;libmex.lib;libmat.lib;%(AdditionalDependencies) 17 | true 18 | true 19 | /EXPORT:mexFunction %(AdditionalOptions) 20 | 21 | 22 | $(IntDir)$(TargetName)$(TargetExt).manifest 23 | 24 | 25 | $(IntDir)$(TargetName)$(TargetExt).manifest.res 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /propertySheets/MATLABx86.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | .mexw32 7 | false 8 | 9 | 10 | 11 | $(MATLAB32_ROOT)\extern\include;%(AdditionalIncludeDirectories) 12 | MATLAB_MEX_FILE;%(PreprocessorDefinitions) 13 | 14 | 15 | $(MATLAB32_ROOT)\extern\lib\win32\microsoft;%(AdditionalLibraryDirectories) 16 | libut.lib;libmx.lib;libmex.lib;libmat.lib;%(AdditionalDependencies) 17 | true 18 | true 19 | /EXPORT:mexFunction %(AdditionalOptions) 20 | 21 | 22 | $(IntDir)$(TargetName)$(TargetExt).manifest 23 | 24 | 25 | $(IntDir)$(TargetName)$(TargetExt).manifest.res 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /propertySheets/README.md: -------------------------------------------------------------------------------- 1 | Visual Studio property sheets for rapidly configuring a project to build a MEX file. 2 | 3 | The usage of the property sheets (.props files) is described at http://stackoverflow.com/a/27391300/2778484. In short, just set the `MATLAB_ROOT` and/or `MATLAB32_ROOT` environment variables (for 32-bit and 64-bit versions, respectively), and include the property sheet (MATLAB.props) into your DLL project: 4 | 5 | ![add MEX property sheet to Visual Studio project](http://i.stack.imgur.com/pnmDB.png) 6 | 7 | The relevant settings configured automatically by the property sheet include: 8 | 9 | 1. Adding `$(MATLAB_ROOT)\extern\include` to the `AdditionalIncludeDirectories` paths (with inherited paths from parent configurations) -- the location of mex.h. 10 | 2. Adding `$(MATLAB_ROOT)\extern\lib\win64\microsoft` to the `AdditionalLibraryDirectories` paths -- the location of libmex.lib, etc. 11 | 3. Listing the libraries: `libut.lib;libmx.lib;libmex.lib;libmat.lib`. 12 | 4. Exporting `mexFunction` (it's a shared library): `/EXPORT:mexFunction`. 13 | 5. Setting the output file extention (e.g. `.mexw64` for x64). 14 | 6. Sets MATLAB_MEX_FILE (helpful to signal to your own headers if they are being used in a MEX file). 15 | 7. Turns on generation of data required for profiling. 16 | 17 | The files MATLABx64.props and MATLABx86.props are for 64-bit and 32-bit MATLAB, respectively. MATLAB.props is supposed to work for either 32-bit or 64-bit MATLAB using conditional expressions, but I have not tested it extensively. 18 | --------------------------------------------------------------------------------