How to use a Plain Old Data structure with C++ Interface when one of the fields is a pointer and has <SHAPE> defined by another field

8 Ansichten (letzte 30 Tage)
I have a Plain Old Data (POD) structure in C, so the fields are public and there are no functions or constructors. One of the fields is a pointer and has <SHAPE> defined by another structure field.
For example, the code in header1.hpp defines the POD structure Data with field data, which is a pointer to a const uint8_t* buffer. The size of data is not known in advance and is defined by another structure field, len. For this example, the code in header1.hpp also defines the create_block function that takes a pointer to Data as an argument and returns the value of len. A simple implementation of create_block is illustrated in header1.cpp.
header1.hpp
#include <cstdint>
#pragma once
struct Data
{
uint32_t offset;
uint32_t len;
const uint8_t* data;
/* some other fields related to the data */
};
int create_block(
const char* filename,
const struct Data* data
);
header1.cpp
#include "header1.hpp"
int create_block(
const char* filename,
const struct Data* data
)
{
// some operation
return data->len;
}
When I publish the MATLAB interface for the above library using C++ interface, with <SHAPE> for the data field defined by the len field, the data field in MATLAB is an empty array and designated as read-only, making it, practically, not useful. How can I use a POD structure with pointer fields in MATLAB in such a way that I can initialize the structure by assigning an array to the pointer field and then pass the structure as argument to library functions?

Akzeptierte Antwort

MathWorks Support Team
MathWorks Support Team am 1 Jun. 2023
For structs (or classes) with pointer (or array) data fields, MATLAB assumes the struct will allocate the array either through a constructor (or method) and will manage the array through the lifetime of the object. MATLAB makes the pointer a read-only property to avoid data field assignment that may result in memory leak of the existing array, if not managed correctly after the assignment. When another structure field is used to define <SHAPE> for the pointer, it also gets designated as read-only.
In order to initialize a POD structure with pointer fields, where one of the fields has <SHAPE> defined by another field, you need to change the interface of your C library or use a wrapper class.
This example illustrates how to use a wrapper class for the C library interface defined in header1.hpp and header1.cpp (above). The wrapper class provides the capability to manage the data buffer and allows you to pass a pointer to Data as a function argument in create_block.
1) In wrapper.hpp, create a class ML_Data that wraps the POD struct Data. The wrapper class  ML_Data inherits from Data and has a constructor and a function setData that manage the buffer referred to by the data field.
wrapper.hpp
#include "header1.hpp"
class ML_Data : public Data
{
public:
ML_Data(int offset, const uint8_t *src, uint32_t len)
{
this->offset = offset;
this->data = nullptr;
this->len = 0;
this->setData(src, len);
}
void setData(const uint8_t *src, uint32_t len)
{
if (this->data != src && len != 0)
{
// clean up existing buffer
if (this->len != 0)
{
delete[] this->data;
this->data = nullptr;
this->len = 0;
}
// allocate new buffer and fill up from 'src' buffer
uint8_t* tempData = new uint8_t[len];
for (uint32_t idx = 0; idx < len; idx++)
tempData[idx] = src[idx];
// refer parent class fields to the new buffer
this->len = len;
this->data = tempData;
}
}
~ML_Data()
{
delete[] data;
}
};
2) Generate the definition file with command below:
>> clibgen.generateLibraryDefinition(["header1.hpp", "wrapper.hpp"], ...
"PackageName","matlab_lib", ...
"TreatObjectPointerAsScalar",true,...
"TreatConstCharPointerAsCString",true,...
"OverwriteExistingDefinitionFiles",true,...
"SupportingSourceFiles","header1.cpp")
3) Configure the definition file to provide <SHAPE> as len for the partial constructs below:
a.    In the section commented as
%% C++ class public data field|data| for C++ class |Data|
Uncomment the addProperty command for data, and change <SHAPE> to "len" (include the quotes).
% For 'len' field in 'Data':
addProperty(DataDefinition, "data", "clib.array.matlab_lib.UnsignedChar", "len");
b.    In the section commented as
%% C++ class constructor for C++ class |ML_Data|
Uncomment the code block beginning with
ML_DataConstructor1Definition = addConstructor(ML_DataDefinition, ...
In the code to define the src argument, change <SHAPE> to "len" (include the quotes).
% For 'src' argument in 'ML_Data' constructor:
defineArgument(ML_DataConstructor1Definition, "src", "clib.array.matlab_lib.UnsignedChar", "input", "len");
c.    In the section commented as
%% C++ class method |setData| for C++ class |ML_Data|
Uncomment the code block starting with
%setDataDefinition = addMethod(ML_DataDefinition, ...
In the code to define the src argument, change <SHAPE> to "len" (include the quotes).
defineArgument(setDataDefinition, "src", "clib.array.matlab_lib.UnsignedChar", "input", "len");
4) Build the definition file.
>> build(definematlab_lib).
Building interface file 'matlab_libInterface.dll' for clib package 'matlab_lib'.
Interface file 'matlab_libInterface.dll' built in folder 'C:\Users\username\MyLibrary\matlab_lib'.
To use the library, add the interface file folder to the MATLAB path.
addpath('C:\Users\username\MyLibrary\matlab_lib')
5. Add the path using the hyperlink or with this command.
>> addpath('C:\Users\username\MyLibrary\matlab_lib')
6. Use the interface in MATLAB.
a. Create an instance of the ML_Data wrapper class with a buffer size of 1000. The wrapper class object ml_data is the same as the POD structure object Data and also initializes the pointer fields with the target array. It can then be used to call functions that have Data as an argument.
>> ml_data = clib.matlab_lib.ML_Data(100, [1:1000]);
Note that, since the size of src is defined by len in the ML_Data constructor, the value of len can be inferred from src. Because of this, the MATLAB C++ Interface signature for the ML_Data constructor only has two arguments: offset and src. The value for len is inferred from src.  
b. Call create_block with ml_data. The data field refers to a buffer of size 1000, as shown in the return value.
>> clib.matlab_lib.create_block("filename.txt", ml_data)
ans = int32 1000
c. Change the data field to refer to another buffer of size 999.
>> ml_data.setData([1:999]);
d. Call create_block with ml_data. Now the return value is 999.
>> clib.matlab_lib.create_block("filename.txt", ml_data)
ans = int32 999
e. Delete the allocated buffer using the destructor.
>> clear ml_data

Weitere Antworten (0)

Tags

Noch keine Tags eingegeben.

Community Treasure Hunt

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

Start Hunting!

Translated by