1 / 26

Mixed code: C++/CLI

Mixed code: C++/CLI. Raffaele Rialdi Visual Developer Security MVP malta@vevy.com MVP Profile: http://snipurl.com/f0cv. http://mvp.support.microsoft.com. Agenda. Perché usare C++ ... perché C++/CLI Carrellata sul linguaggio solo gli elementi del linguaggio utili per interop

tracey
Download Presentation

Mixed code: C++/CLI

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Mixed code: C++/CLI Raffaele RialdiVisual Developer Security MVP malta@vevy.comMVP Profile: http://snipurl.com/f0cv http://mvp.support.microsoft.com

  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 CLR Features • Garbage collector, finalizzatori • Generics • Reference e Value type • Interfacce • Verificabilità • Security • Proprietà, delegati, eventi C++ Features • Finalizzazione deterministica • Template e generics • Uso dei tipi nativi • Multiple inheritance (unmanaged) • STL, algoritmi generici • Distinzione Puntatore/Puntato • Copy construction, assignment

  5. Novità in C++/CLIdi 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 Le specifiche si trovano qui: http://msdn.microsoft.com/visualc/homepageheadlines/ecma/default.aspx

  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 {...} ; #pragma managed ...

  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 Pointerinterior_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 Pointerpin_ptr<T> Unmanaged PointerT*

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

  13. Marshalling di stringhe

  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 memoriamanaged 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 funzionied 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  managedQuesto 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 laconvenzione __clrcall • forzando __clrcall si evita il double thunking virtual return-type__clrcallfunction-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 C++ Linker D:\>cl /c /clr a.cpp 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 aggiungole 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?

More Related