Salut,
Sémantiquement le but est de faire ça :
Sauf que ça ne fonctionne pas.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 #include <gcroot.h> void Test (String ^% k) { k = "apres"; } int main(array<System::String ^> ^args) { gcroot<String^> ungcroot = "avant"; Test(ungcroot); String ^res = ungcroot; // res == "apres" return 0; }
La solution que j'ai trouvée :
Une autre solution ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 #include <gcroot.h> template <typename T> union Dummy { // pour récupérer le champ caché _handle de la structure gcroot gcroot<T>& g; void *ptr; Dummy(gcroot<T>& g) : g(g) {} }; template <typename T> T% Gcroot2TrackingReference(gcroot<T>& x) { Dummy<T> d(x); /// x est un pointeur sur gcroot, /// un gcroot est un pointeur sur Object^ return **(T**) d.ptr; // d'où ce cast } void Test (String ^% k) { k = "apres"; } int main(array<System::String ^> ^args) { gcroot<String^> s = "avant"; Test(Gcroot2TrackingReference(s)); String^ res = s; // res == "apres" return 0; }
Je peux argumenter sur le fait que c'est 100% safe de faire ça avec l'implémentation actuelle de la CLR :
- un Object ^ est un pointeur dont l'emplacement (l'adresse) est toujours connue de la CLR, qui modifiera sa valeur si elle le souhaite avec ses histoires de déplacement d'objets dans le tas managé. Seules les fonctions __clrcall peuvent mettre un Object^ sur la pile C++ habituelle : la CLR sait retrouver toutes les fonctions __clrcall de la callstack.
- un Object^% est une référence sur Object^ donc c'est l'adresse d'un Object^. Deux cas se présentent : le Object^ est sur la pile C++ et ne bougera pas, le Object^% est alors une référence C++ normale. Ou bien le Object^ est un champ d'un objet managé, et alors Object^% est une tracking référence. La CLR n'a pas de raison de présumer d'où est la pile C++. Elle sait juste reconnaître quand une adresse est dans le tas managé ou non. Un Object^% peut lui être situé n'importe où, et l'Object^ référencé également (alors que l'Object lui est forcément dans le tas managé).
- un gcroot est un pointeur sur Object^, Object^ qui lui est situé à un emplacement (plus ou moins alloué dynamiquement) connu de la CLR.
le gcroot peut être situé n'importe où, comme un Object^%.- La seule différence entre un gcroot et une tracking reference c'est qu'un gcroot pointe toujours sur un Object^ situé dans cet emplacement connu de la CLR et alloué dynamiquement, alors qu'une tracking référence pointe sur un Object^ situé n'importe où.
- Donc caster un gcroot en Object^% n'est pas unsafe. L'inverse l'est peut-être.
Partager