implementation.user - moduleus/urx GitHub Wiki

Comparison

C++

Les opérateurs == et != sont implémentés pour toutes les classes UAC/URX.

If a class can be using inside a weak_ptr or shared_ptr, you need to add header urx/detail/compare.h. This header overrides comparison for smart pointers. You will make a deep comparison and not a pointer value comparison.

Si vous souhaitez forcer la comparaison des pointeurs, il faut le faire de façon explicite:

  • Shared pointer
std::shared_ptr<...> data;
data.get() == ...
  • Weak pointer
std::weak_ptr<...> data;
if (data.expired()) {
  // A considérer comme non assigné ou comme expiré
} else {
  data.lock().get();
}

MATLAB

Si vous voulez utiliser la comparaison des handle MATLAB, utilisez l'opérateur == et ~=.

Si vous effectuer une comparaison en profondeur, utilisez la fonction isequaln. Il est aussi possible d'utiliser la fonction isequal mais son comportement est identique à isequaln.

Gestion de la mémoire

Différents types d'allocation en C++

  • Raw
Transform transform;

Si transform est une variable dans le corps d'une fonction, la mémoire est allouée dans la pile et sera libérée à la fermeture du bloc ({...}).

Si la variable est un membre d'une structure, elle sera allouée en même temps que la structure et a la même durée de vie que la structure.

  • Shared pointer
std::shared_ptr<urx::Dataset> dataset = std::make_shared<urx::Dataset>();

La mémoire est allouée dans le tas. Elle sera libérée lorsque tous les std::shared_ptr seront détruits.

La syntaxe std::shared_ptr<urx::Dataset> dataset; déclare un shared pointer qui contient un pointeur null. Il est impératif d'appeler le constructeur avec la méthode std::make_shared.

  • Weak pointer
std::shared_ptr<urx::ElementGeometry> element_geometry = std::make_shared<urx::ElementGeometry>();
std::weak_ptr<urx::ElementGeometry> element_geometry_weak = element_geometry;

Il s'agit d'un pointeur vers la mémoire allouée dans un shared pointer. Si tous les objets shared pointer sont détruits, le pointeur stocké dans l'objet weak pointer deviendra invalide.

  • Optional
std::optional<TriggerIn> trigger_in = uac::TriggerIn;

Si trigger_in est une variable dans le corps d'une fonction, la mémoire est allouée dans la pile et sera libérée à la fermeture du bloc ({...}).

Si la variable est un membre d'une structure, elle sera alloué en même temps que la structure.

La différence avec un objet de type Raw, c'est que, même si la mémoire est allouée, l'objet n'est pas construit tant que le constructeur n'a pas été appelé explicitement. Ainsi, std::optional<TriggerIn> trigger_in déclare une variable trigger_in qui vaut std::nullopt.

Allocation.

Toutes les données sont stockées dans un objet racine : dataset.

Cet objet doit être alloué par : - en C++ : auto dataset = std::make_shared<urx::Dataset>(); - en MATLAB : dataset = urx.Dataset(); - en Python : dataset = urx.Dataset();

Type de stockage des données

Quelque soit l'implémentation utilisée (C++, MATLAB ou Python), toutes respectent l'implémentation en C++. Ainsi, si le champ d'une structure en C++ est déclarée en weak pointer, elle se comportera de la même façon en MATLAB et Python.

Tous les membres qui sont déclarés en weak pointer doivent pointer vers un shared pointer préalablement stocké dans son champ dédié (voir la description de chaque champ weak pointer pour connaitre le champ shared pointer dédié).

MATLAB

Toutes les données allouées par MATLAB sont forcément des shared pointers.

Cependant, lorsque vous assignez un objet UAC/URX dans un membre d'un autre objet UAC/URX, les deux éléments peuvent être du même type mais ne pas avoir la même allocation mémoire.

Assignation d'un objet shared vers un objet raw

dataset = urx.Dataset();
version = urx.Version();
version.minor = 1;
version.major = 2;
version.patch = 3;
dataset.version = version;

dataset et version sont des shared pointer et dataset.version est un raw pointer.

A la ligne dataset.version = version, les données de version sont copiées dans le membre version de dataset.

Chaque objet UAC/URX ayant un handle, et afin de garder une cohérence des données, la variable version sera automatiquement modifiée et converti en raw pointer et pointera vers dataset.version.

Assignation d'un objet shared vers un objet weak

element=urx.Element;
elementGeometry=urx.ElementGeometry;
element.elementGeometry = elementGeometry;

element et elementGeometry sont des shared pointer et element.elementGeometry est un weak pointer.

A la ligne element.elementGeometry = elementGeometry, le shared pointer elementGeometry est assigné au champ elementGeometry de element.

Comme tous les deux sont des pointeurs, la variable elementGeometry reste un shared pointer n'est pas converti en weak pointer. En effet, si le shared pointer était converti en weak pointer, la mémoire serait libérée car plus aucun objet de type shared pointer ne stockerait l'allocation mémoire de l'élément géometrie.

Cependant, il reste important de stocker la variable elementGeometry dans le champ elementGeometries de sa sonde et que sa sonde soit stocké dans le dataset global.

probe = urx.Probe;
probe.elementGeometries = elementGeometry;
dataset = urx.Dataset;
dataset.probes = probe;

Insert shared object in a vector that holds only raw object.

Urx MATLAB classes don't hold any value. They are read in real time from the C++ back-end. So there is some limitations with C++ vectors.

acquisition = urx.Acquisition();
groupData = urx.GroupData();
acquisition.groupsData = [groupData, groupData];

At first, groupData is a shared object because it's allocated by MATLAB.

By design, acquisition.groupsData holds only raw objects. groupData is copied to every index in acquisition.groupsData.

After the assignment, groupData is a raw object and points to the last index where it's used in acquisition.groupsData.

Automatic C++ reallocation

In C++, when you change the size of a vector, it may realloc memory and user can't manage it.

acquisition = urx.Acquisition();
groupData1 = urx.GroupData();
acquisition.groupsData = [groupData1];
groupDataRawRef = acquisition.groupsData;
groupData2 = urx.GroupData();
acquisition.groupsData = [groupData2, groupData2];

At first, groupData1 is a shared object. After its insertion in acquisition.groupsData, groupData1 is converted to a raw object.

groupDataRawRef is a raw object that point to the same data than groupData1.

When acquisition.groupsData is reassign with a new size, the C++ vector need to do a re-allocation in memory.

At this moment, groupDataRawRef points to an invalid data. Using this object with throw an exception with message: Failed to get property XXXX. Object doesn't belong to urx.Acquisition.groupsData anymore. The vector has changed or the C++ vector has reallocated memory and the object has became invalid..

Python

shared_ptr and weak_ptr

Pybind11 handles pointers as shared_ptr by default.

Python doesn't support weak_ptr. The default behavior for getting a weak_ptr is the same as getting a shared_ptr except that a runtime_error exception is raised if the pointer has expired.

Class urx.RawData

Each GroupData have data in field raw_data.

It's the only field that have complex class: RawData. It's an interface with 4 getters : getBuffer, getSize, getSamplingType and getDataType. getBuffer returns a void* pointer that is not const. But once the RawData class is allocated, you can't grow or shrink it, only modify it.

RawData may be allocated come from multiple source, you can have:

  • RawDataVector to have a std::vector as buffer. When you resize(), all value are initialized by 0.
  • RawDataNoInit an array that don't initialize data.
  • RawDataWeak to store a pointer without managing life cycling. It doesn't store a std::weak_ptr, only a pointer.
⚠️ **GitHub.com Fallback** ⚠️