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 views (last 30 days)
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?

Accepted Answer

MathWorks Support Team
MathWorks Support Team on 8 Aug 2023
Edited: MathWorks Support Team on 10 Oct 2025 at 20:56
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
%% 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");
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.
% Create a MATLAB struct similar to the C++ Header struct
mlHeader.a = 5;
mlHeader.b = 3;
% Use the MATLAB struct to create a C++ Header struct
cHeader = clib.matlab_lib.Header(mlHeader);
% Create a MATLAB struct to match the C++ Data struct
mlData.offset = uint32(1);
mlData.data = uint8(1:10);
% Use MATLAB struct to create a C++ structs
cData = clib.matlab_lib.Data(mlData)
% Call create_block using the C++ Data struct
clib.matlab_lib.create_block("filename.txt", cHeader, cData)
Or you can call "create_block" directly with the MATLAB structs
% Call create_block using the MATLAB structs
clib.matlab_lib.create_block("filename.txt", mlHeader, mlData)
For more information on struct support, see the Pass struct Parameter documentation.
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
%% 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

More Answers (0)

Tags

No tags entered yet.

Community Treasure Hunt

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

Start Hunting!