Clear Filters
Clear Filters

How to access raw data via the mex c++ api?

25 views (last 30 days)
I want to use the mex c++ api to interface existing external code. The API of the external application is fairly low level, but I cannot work out how I can access the double pointers I'd like to pass. As this is not specific to the code I have, assume I have a function
void foo(int n, const double *a, const double *b, double *c)
to which I pass the arrays a and b and their lengths n and it calculates something and puts it onto the array c.
In order to use the mex function with the c++ api I don't know how I can avoid copying the data into some dummy pod arrays along the lines of:
#include "mex.hpp"
#include "mexAdapter.hpp"
using namespace matlab::data;
using matlab::mex::ArgumentList;
class MexFunction : public matlab::mex::Function
{
private:
ArrayFactory factory;
std::shared_ptr<matlab::engine::MATLABEngine> matlabPtr;
void validateArguments(ArgumentList outputs, ArgumentList inputs)
{
if (inputs.size() != 2)
matlabPtr->feval(u"error", 0, std::vector<Array>({ factory.createScalar("Require 2 inputs")}));
if (inputs[0].getType() != ArrayType::DOUBLE || inputs[0].getType() == ArrayType::COMPLEX_DOUBLE )
matlabPtr->feval(u"error", 0, std::vector<Array>({ factory.createScalar("First input has to be a double array") } ));
if (inputs[1].getType() != ArrayType::DOUBLE || inputs[1].getType() == ArrayType::COMPLEX_DOUBLE )
matlabPtr->feval(u"error", 0, std::vector<Array>( {factory.createScalar("second input has to be a double array")}));
if (outputs.size() > 1)
matlabPtr->feval(u"error", 0, std::vector<Array>( {factory.createScalar("One output only")}));
}
void foo(int n, const double *a, const double *b, double *c){
int i;
for (i=0;i<n;i++)
c[i] = a[i] + b[i];
}
public:
MexFunction() : matlabPtr(getEngine()) {}
~MexFunction() {}
void operator() (ArgumentList outputs, ArgumentList inputs)
{
validateArguments(outputs,inputs);
double *retVal, *a, *b;
int i, n = inputs[0].getNumberOfElements();
retVal = new double[n];
a = new double[n];
b = new double[n];
for (i =0; i<n;i++){
a[i] = inputs[0][i];
b[i] = inputs[1][i];
}
foo(n, a, b, retVal);
ArrayDimensions dims = inputs[0].getDimensions();
outputs[0] = factory.createArray(dims, retVal, retVal+n);
delete a;
delete b;
delete retVal;
}
};
This copying and creating of placeholders seems unelegant and wasteful. What am I missing? Please help!
Thank you!
Manuel

Accepted Answer

Mihir Thakkar
Mihir Thakkar on 3 Jan 2019
There is a "release" member function that returns a unique pointer of the raw data: TypedArray Documentation.
Instead of copying data from the typed array, the following should just work:
double* a=inputs[0].release().get();
  2 Comments
Dominic Liao-McPherson
Dominic Liao-McPherson on 8 Nov 2019
The release documentation says that
"Release the underlying buffer from the Array. If the Array is shared, a copy of the buffer is made; otherwise, no copy is made. After the buffer is released, the array contains no elements."
What does this imply when using .release().get()? Won't the unique_ptr returned by release() immediately go out of scope (since its not being assigned to anything) and thus the memory pointed to by
```double* a = inputs[0].release().get();```
will become invalid? Or is my understanding of unique_ptr flawed?
Of course just assigning
std::unique_ptr<double> temp = inputs[0].release();
double* a = temp.get();
Would resolve the issue.
Torsten Knüppel
Torsten Knüppel on 4 Apr 2020
Mihir could you please elaborate your answer?
It would be really great if it was so simple to access the raw data, but I do share Dominic's concerns. Furthermore, I don't manage to get your code working, because matlab::data::Array has no release-function. Following the answers below, it should be something along the line
double* b = matlab::data::TypedArray<double>(inputs[0]).release().get()
Furthermore, I don't fully understand the documentation - under which cirumstances does this create a copy (because I think this is what everybody wants to avoid). According to the documentation this is done, when the array is -shared-. Is this to be understood in the sense of a shared-pointer as the existence of multiple references to the array? I have done a test: I passed an array to the Mex-function and then released the memory with code above. After returning to Matlab the array that I passed was unchanged (which I was glad to see). However, I would have expected that somehow the array is destroyed by calling release, because this somehow detaches the data from the array (at least that's how I understand it).
Does this mean, that Matlab is passing the data by copy or does it keep a reference to the array (so that the data is copied when I call release). Wouldn't that mean, that I can't avoid a copy when using the mechanism you suggest?
Thanks in advance!

Sign in to comment.

More Answers (1)

Savyasachi Singh
Savyasachi Singh on 14 Feb 2020
Edited: Savyasachi Singh on 14 Feb 2020
I am extracting the pointer to the underlying data for TypedArray<T> using the following code. The trick is to extract the pointer from TypedIterator<T>.
//! Extracts the pointer to underlying data from the non-const iterator (`TypedIterator<T>`).
/*! This function does not throw any exceptions. */
template <typename T>
inline T* toPointer(const matlab::data::TypedIterator<T>& it) MW_NOEXCEPT {
static_assert(std::is_arithmetic<T>::value && !std::is_const<T>::value,
"Template argument T must be a std::is_arithmetic and non-const type.");
return it.operator->();
}
/*! Extracts pointer to the first element in the array.
* Example usage:
* \code
* ArrayFactory factory;
* TypedArray<double> A = factory.createArray<double>({ 2,2 }, { 1.0, 3.0, 2.0, 4.0 });
* auto ptr = getPointer(A);
* \endcode
* \note Do not call `getPointer` with temporary object. e.g., the following code is ill-formed.
* auto ptr=getPointer(factory.createArray<double>({ 2,2 },{ 1.0, 3.0, 2.0, 4.0 }));
*/
template <typename T>
inline T* getPointer(matlab::data::TypedArray<T>& arr) MW_NOEXCEPT {
static_assert(std::is_arithmetic<T>::value, "Template argument T must be a std::is_arithmetic type.");
return toPointer(arr.begin());
}
template <typename T>
inline const T* getPointer(const matlab::data::TypedArray<T>& arr) MW_NOEXCEPT {
return getPointer(const_cast<matlab::data::TypedArray<T>&>(arr));
}
Add the following #includes
  1. type_traits
  2. MatlabDataArray.hpp
Use the function getPointer as follows
ArrayFactory factory;
TypedArray<double> A = factory.createArray<double>({ 2,2 }, { 1.0, 3.0, 2.0, 4.0 });
auto ptr = getPointer(A); // double*
const TypedArray<double> B = factory.createArray<double>({ 2,2 }, { 1.0, 3.0, 2.0, 4.0 });
auto ptr = getPointer(B); // const double*
// Assuming that inputs[0] is a double array
const TypedArray<double> inArr(inputs[0]);
auto ptr = getPointer(inArr); // const double*
This approach has been working fine for me. Do not call getPointer function on a temporary TypedArray object.
  4 Comments
Vincent Huber
Vincent Huber on 24 Sep 2020
This getPointer function is fantastic but using the static_assert(std::is_arithmetic<T>::value) with T = std::complex<double> does not work and should be adapted.
Simon Müller
Simon Müller on 2 Jun 2021
Does this allow modifying the underlying data? Or does it only allow reading? Or would I have to use the following method if I want to modify the data?
double* a=inputs[0].release().get();

Sign in to comment.

Categories

Find more on Structures in Help Center and File Exchange

Products


Release

R2018b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!