La presentazione è in caricamento. Aspetta per favore

La presentazione è in caricamento. Aspetta per favore

Mixed code: C++/CLI Raffaele Rialdi Visual Developer Security MVP

Presentazioni simili


Presentazione sul tema: "Mixed code: C++/CLI Raffaele Rialdi Visual Developer Security MVP"— Transcript della presentazione:

1 Mixed code: C++/CLI Raffaele Rialdi Visual Developer Security MVP
MVP Profile:

2 Agenda Perché usare C++ ... perché C++/CLI Carrellata sul linguaggio
solo gli elementi del linguaggio utili per interop gestione della memoria managed/unmanaged pinning e marshalling mixed types C++ future directions

3 Perché usare C++ Rende semplice usare codice unmanaged
Le definizioni PInvoke non sono sempre semplici da scrivere VC++ usa IJW (It Just Works) per usare codice nativo e codice managed allo stesso tempo Ad esempio si semplifica l'accesso alle librerie DirectX VC++ può creare dll/exe misti con codice C# / VB / C++ Rende semplice usare codice managed System.Xml semplifica la manipolazione di Xml System.Net semplifica l'accesso ai socket e al web System.Text.RegularExpressions semplifica le regex Idem per altre namespace/classi del framework

4 Perché un nuovo linguaggio? Perché C++/CLI?
Le managed extensions erano complesse per aderire alle regole dello standard ISO (doppio underscore, etc.) cl /clr:oldSyntax continua a compilare le managed extensions un tool di Stan Lippman permette una migrazione quasi automatica C++/CLI è vicino alla standardizzazione ECMA (in questi giorni) ed ISO (probabilmente chiamata ISO C++09) C++/CLI ha una piacevole sintassi per lavorare sia con il mondo managed che unmanaged C++ Features Finalizzazione deterministica Template e generics Uso dei tipi nativi Multiple inheritance (unmanaged) STL, algoritmi generici Distinzione Puntatore/Puntato Copy construction, assignment CLR Features Garbage collector, finalizzatori Generics Reference e Value type Interfacce Verificabilità Security Proprietà, delegati, eventi

5 Novità in C++/CLI di cui non parleremo
trivial properties: property String ^Name; indexed properties managed copy constructors delegate + event managed operator overloading boxing/unboxing safe_cast<> generics vs templates method overriding lock(...) jagged arrays STL.NET integrazione MFC / Winform integrazione Avalon compilazione parallela profile guided optimization OpenMP parallelism CLR Delay Loading

6 Veloce carrellata su C++/CLI
Supporto distinto per tipi managed e unmanaged C++/CLI C# public ref class MyClass { ... } ; public ref struct MyClass { ... } ; MyClass ^c = gcnew MyClass(); public class MyClass { ... } MyClass c = new MyClass(); public value class MyValueType { ... } ; public value struct MyValueType { ... } ; MyValueType v; public struct MyValueType { ... } public interface class MyInterface { ... } ; public interface struct MyInterface { ... } ; public interface MyInterface { ... } public enum class MyEnum : char { ... } ; public enum struct MyEnum : char { ... } ; public enum MyEnum : char { ... } array<int> ^nums = {1,2,3} ; int[] nums = new int[] {1,2,3} Le specifiche si trovano qui:

7 I nuovi operatori ^ e % Introdotto nel linguaggio l' "handle"
una sorta di puntatore managed ad un oggetto nel managed heap Il CLR tiene aggiornato il suo valore quando esegue la GC analogo del reference di C#, ma il reference in C++ esisteva già il simbolo è "hat" ^ su un handle si applicano gli operatori -> e * L'analogo di void* è Object^ Nuovo allocatore di memoria managed gcnew String ^s1 = gcnew String; String s2; continua ad essere un espressione valida Introdotto nel linguaggio il "tracking reference" Analogo del reference & di C++ classico, cioè un alias all'oggetto il simbolo è "%"

8 Distruzione deterministica
C++/CLI introduce la distruzione deterministica delle risorse Non deve e non può riguardare la memoria, ma solo le risorse unmanaged. Questo è lo scopo del pattern Dispose. In pratica il Pattern Dispose viene implementato dal compilatore Implementazione completa di GC.SuppressFinalize Quando nella classe esiste il distruttore: In sostanza il distruttore della classe viene mappato su Dispose La classe implementa automaticamente IDisposable L'uscita dallo scope o una delete esplicita provoca la chiamata a Dispose (analogo dello statement using di C#, ma più semplice) Introdotto anche la sintassi per il finalizzatore La sintassi è analoga al distruttore  !NomeClasse() {...} Nel finalizzatore si mette la distruzione delle risorse Nella Dispose si mette la chiamata al finalizzatore

9 Istruire il precompilatore
Il compilatore VC++ accetta di mixare codice managed e unmanaged anche nello stesso listato Alcune volte potrebbe esserci ambiguità su come compilare il codice Si può informare il compilatore con due #pragma #pragma managed #pragma unmanaged #pragma managed class Managed {...} ; #pragma unmanaged class Native {...} ; ...

10 Memoria: Interior Pointers
Al contrario dell'handle, permette l'aritmetica dei puntatori Utile per la veloce manipolazione di array e buffer Trasparente: è usabile anche per tipi unmanaged (restituisce un puntatore classico) interior_ptr<type> name = &value; Esempio 1 array<int>^a = {1,2,3,4,5}; interior_ptr<int> ip = &a[0]; for(int i = 0; i<a->Length; i++) Console::WriteLine(++ip[i]); // output: 2, 3, 4, 5, 6 Esempio 2 String ^str1 = "Hello, world"; String ^str2 = str1; interior_ptr<String^> ip = &str1; *ip = "Ciao"; Console::WriteLine(str1 + " - " + str2); // output: Ciao – Hello, world

11 Memoria: Pinning Pointers
pin_ptr<type> name = &value; void F(int* p); // Func unmanaged array<int>^ arr = …; pin_ptr<int> pi = &arr[0]; F(pi); // ptr unmanaged Interior Pointer interior_ptr<T> String ^str1 = "Hello, world"; // interior pointer al buffer della stringa (non è una copia) interior_ptr<const wchar_t> ip = PtrToStringChars(str1); // interior pointer senza 'const' interior_ptr<wchar_t> ip2 = const_cast<interior_ptr<wchar_t> >(ip); // pinning pointer  Il GC non può muovere il buffer pin_ptr<wchar_t> pp = ip2; // modifico il buffer for(int i=0; i<str1->Length; i++) ++pp[i]; // caratteri ascii incrementati Console::WriteLine(str1); // out Ifmmp-!xpsme Pinning Pointer pin_ptr<T> Unmanaged Pointer T*

12 Sguardo molto semplicistico in memoria
Unmanaged heap memoria classica sempre ferma sizeof(MyClass1) // pc è un "puntatore" MyClass1 *pc = new MyClass1(0x30); gg.hh.jj.kk MyClass1 &rc = *pc; // rc è un "reference"  alias gg.hh.jj.kk MyClass1 *pc2 = pc; Stack locale Managed heap GC muove i blocchi di memoria GC aggiorna i valori quando muove la memoria xx.yy.zz.tt size unknown MyClass2 ^hc = gcnew MyClass2(); // hc è un handle. ^ si pronuncia hat pp.qq.rr.ss interior_ptr<MyClass2 ^> ip = &hc; // ip è un "interior pointer" pinned pin_ptr<MyClass2 ^> pp = &hc; pin_ptr<MyClass2 ^> pp2 = pp; ll.mm.nn.oo // pp e pp2 sono "pinning pointers" MyRefType %tr = *hc; // tr è un "tracking reference"

13 Marshalling di stringhe
Stringhe Ansi Managed  Unmanaged Unmanaged  Managed Marshal::StringToHGlobalAnsi Marshal::PtrToStringAnsi Stringhe Unicode Managed  Unmanaged Unmanaged  Managed PtrToStringChars (#include <vcclr.h>) Marshal::PtrToStringUni Stringhe COM (BSTR) Managed  Unmanaged Unmanaged  Managed Marshal::StringToBSTR Marshal::PtrToStringBSTR

14 È tutto così semplice? ... quasi
Fin ad ora abbiamo visto che: Creare immagini miste managed/unmanaged è semplice Eseguire il marshalling dei parametri è semplice Ci sono semplici strumenti per accedere alla memoria managed e unmanaged L'interoperabilità è possibile in due modi: P/Invoke esplicito (come in C#) IJW (=It Just Works) eseguendo il marshalling dei parametri E allora dov'è il problema?

15 Mixed types are not supported
public ref class RefClass { public: POINT pt; // unmanaged struct }; public class Native { public: System::String ^str; }; error C4368: cannot define 'pt' as a member of managed 'ManagedClass': mixed types are not supported error C3265: cannot declare a managed 'str' in an unmanaged 'Native'

16 Tipi misti: tipi managed dentro tipi unmanaged
GCHandle gcroot<> necessita #include<vcclr.h> non chiama automaticamente Dispose! msclr::auto_gcroot<> necessita #include <msclr\auto_gcroot.h> chiama automaticamente la IDisposable::Dispose se esiste #include <vcclr.h> using namespace System; public class Native1 { gcroot<String ^> str; }; #include <msclr\auto_gcroot.h> using namespace msclr; using namespace System; public class Native2 { auto_gcroot<String ^> str; };

17 Tipi misti: tipi unmanaged dentro tipi managed
Brutta notizia: fin'ora nessun supporto ufficiale ma la soluzione è molto semplice .... In una classe managed si può avere un puntatore unmanaged ma è poi necessario gestire la sua distruzione (ciclo di vita) Molto meglio scrivere una classe con template che gestisce il ciclo di vita del puntatore Brandon Bray (uno degli ideatori della nuova sintassi) ne ha pubblicata una chiamata "Embedded" sul suo blog #include <windows.h> #include "Embedded.h" public ref class RefClass { Embedded<POINT> np; };

18 Cosa sono le calling convention?
Una sorta di contratto alla compilazione che prevede: come passare gli argomenti delle funzioni ed il valore di ritorno quali registri della CPU devono essere salvati Le quattro convenzioni più usate oggi sono: __cdecl usato dalle librerie C e numerose API __stdcall conosciuta anche come "pascal", usata dalle Win32 API __fastcall usa i registri per passare gli argomenti __thiscall default per le chiamate a funzioni membro in C++

19 Cos'è il "double thunking"?
Quando si compila codice con metadati ogni funzione ha due entry-point: uno con la calling-convention assegnata uno con la calling-convention CLR Quale viene usato? se il codice è compilato con /clr, l'entry-point di base è un thunk alla chiamata CLR se il codice è compilato senza /clr, l'entry point CLR è un thunk alla chiamata x86 Come viene scelto l'entry-point da usarsi? il compilatore è normalmente in grado di scegliere ma ... non può scegliere se la chiamata è un puntatore a funzione non può scegliere anche per le funzioni virtuali perché queste sono puntatori a funzioni

20 Cos'è il "double thunking"?
Dove si presenta il problema? Le funzioni virtuali compilate in IL avranno sempre un thunk da unmanaged a managed. Questo è inevitabile. Se poi la chiamata viene fatta da codice managed, c'è un thunk supplementare: managed  unmanaged  managed Questo doppio passaggio si chiama "double thunking" Esiste una soluzione? La soluzione esiste solo se quella chiamata virtuale verrà solo chiamata dal mondo managed In questo caso è sufficiente marcare la funzione con la convenzione __clrcall forzando __clrcall si evita il double thunking virtual return-type __clrcall function-name(arguments);

21 Un assembly, mixed language
Task complesso, nessun supporto di VS2005 Più semplice se si disabilitano i precompiled headers in tutti i progetti VC++ (ma è comunque usarli) La novità consiste nei .netmodule Il .netmodule è identico ad un assembly ma senza metadati per esempio non ha versione Il netmodule viene ri-compilato al link time Solo il linker di C++ ha questa capacità di ricompilazione a.cpp C++ Compiler a.obj EXE C++ Code D:\>cl /c /clr a.cpp C++ Linker C# Code D:\>csc /t:module c.cs c.cs C# Compiler c.netmodule

22 Un assembly, mixed language
Esempio di una Winform C# che usa una business logic in C++/CLI Progetto 1: CppLogicClassLibrary Per semplicità precompiled headers disabilitati Si compila con VS.net EXE CppLogicClassLibrary CsFormClassLibrary CppStartWinform

23 Un assembly, mixed language
Progetto 2: CsFormClassLibrary Eliminato Program.cs, l'entry point sarà in C++/CLI Si referenzia CppLogicClassLibrary e si usano le classi Si compila in VS.NET solo per il controllo sintattico Necessario compilare a mano  (ma si può lanciare make.bat come post-build action) Compilatore C# vogliamo un .netmodule dipendenza dal progetto C++/CLI csc /t:module /addmodule:..\CppLogicClassLibrary\debug\CppLogicClassLibrary.obj /resource:obj\Debug\CsFormClassLibrary.Form1.resources *.cs compilo tutti i sorgenti aggiungo le risorse (form)

24 Un assembly, mixed language
Progetto 3: CppStartWinform Progetto C++/CLI Winform a cui si toglie la form Cambiare le opzioni da /clr:safe a /clr Aggiungere nel Linker – Input – Additional il .netmodule di C# e l'obj di C++ Aggiungere alla command line del linker l'opzione /LTCG Funge solo da entry point per l'applicazione managed Si può fare la build da VS.NET Risultato: 1 Assembly EXE con dentro tre immagini miste native/managed Ovviamente la dipendenza dal framework rimane

25 Qual'è il futuro di ISO C++?
Ci sono problemi da risolvere per il cambio nell'evoluzione della crescita hardware niente più grossi aumenti di velocità nelle CPU aumento del numero di 'core' nelle CPU L'accesso diretto alla memoria impedisce una gestione efficiente nel determinare i problemi di concorrenza Work in progress su: gestione automatica della concorrenza ("concurs") gestione asincrona ("Futures") type inference lambda functions Linq

26 Domande?


Scaricare ppt "Mixed code: C++/CLI Raffaele Rialdi Visual Developer Security MVP"

Presentazioni simili


Annunci Google