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.
The workaround for this issue depends on which MATLAB release you are using.
R2024b or later:
If you are able to modify the "Data" struct to remove the "const" requirement on the "data" field, then you can use a MATLAB struct to create the "Data" clib type.
1) Modify the "header1.hpp" file to remove the "const" requirement on the "data" field.
struct Data
{
uint32_t offset;
uint32_t len;
uint8_t* data; /* No longer const */
/* some other fields related to the data */
};
2) Generate the definition file with command below:
>> clibgen.generateLibraryDefinition("header1.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:
In the section commented as
Uncomment the addProperty command for data, and change <SHAPE> to "len" (include the quotes).
addProperty(DataDefinition, "data", "clib.array.matlab_lib.UnsignedChar", "len");
4) Build the definition file.
>> build(definematlab_lib)
5. Add the path using the hyperlink or with this command.
>> addpath('C:\Users\username\MyLibrary\matlab_lib')
5. Use MATLAB structs to create the "Header" and "Data" objects in the interface.
mlHeader.a = 5;
mlHeader.b = 3;
cHeader = clib.matlab_lib.Header(mlHeader);
mlData.offset = uint32(1);
mlData.data = uint8(1:10);
cData = clib.matlab_lib.Data(mlData)
clib.matlab_lib.create_block("filename.txt", cHeader, cData)
Or you can call "create_block" directly with the MATLAB structs
clib.matlab_lib.create_block("filename.txt", mlHeader, mlData)
R2024a and earlier or if "data" must be a "const" type: 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
Uncomment the addProperty command for data, and change <SHAPE> to "len" (include the quotes).
addProperty(DataDefinition, "data", "clib.array.matlab_lib.UnsignedChar", "len");
b. In the section commented as
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).
defineArgument(ML_DataConstructor1Definition, "src", "clib.array.matlab_lib.UnsignedChar", "input", "len");
c. In the section commented as
Uncomment the code block starting with
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.