필터 지우기
필터 지우기

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

조회 수: 27 (최근 30일)
Manuel Schaich
Manuel Schaich 2018년 12월 22일
댓글: Simon Müller 2021년 6월 2일
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

채택된 답변

Mihir Thakkar
Mihir Thakkar 2019년 1월 3일
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
Dominic Liao-McPherson
Dominic Liao-McPherson 2019년 11월 8일
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 2020년 4월 4일
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!

댓글을 달려면 로그인하십시오.

추가 답변 (1개)

Savyasachi Singh
Savyasachi Singh 2020년 2월 14일
편집: Savyasachi Singh 2020년 2월 14일
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
Vincent Huber
Vincent Huber 2020년 9월 24일
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 2021년 6월 2일
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();

댓글을 달려면 로그인하십시오.

카테고리

Help CenterFile Exchange에서 MATLAB Data API for C++에 대해 자세히 알아보기

제품


릴리스

R2018b

Community Treasure Hunt

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

Start Hunting!

Translated by