Co je C++/CLI
C++/CLI (C++ for the Common Language Infrastructure) je rozšíření standardního C++ umožňující tvorbu spravovaného kódu pro CLR (.NET runtime). Kombinuje nativní C++ výkon s přístupem k celé .NET knihovně.
Klíčové vlastnosti
- Přímý přístup k .NET Base Class Library (BCL)
- Interoperabilita s existujícím nativním C/C++ kódem (It Just Works – IJW)
- Garbage collector CLR spravuje spravované objekty; nativní paměť stále manuálně
- Jeden soubor může mít managed i unmanaged kód současně
- Kompilátor: MSVC s přepínačem
/clrnebo/clr:pure
C++/CLI – hello world
#include <iostream> using namespace System; int main() { // Spravovaný výstup (.NET) Console::WriteLine("Ahoj z .NET!"); // Nativní výstup (C++) std::cout << "Ahoj z native C++!" << std::endl; return 0; }
Ekvivalent v C#
using System; class Program { static void Main() { Console.WriteLine("Ahoj z .NET!"); // Nativní kód není přímo přístupný } }
| Přepínač kompilátoru | Popis |
|---|---|
| /clr | Smíšený kód (native + managed) – nejběžnější pro interop |
| /clr:pure | Pouze IL (spravovaný), ale může volat nativní DLL |
| /clr:safe | Čistě verifikovatelný IL (omezené možnosti) |
| /clr:netcore | Cílí na .NET Core / .NET 5+ (VS 2019 16.4+) |
Projekt a kompilace
Vytvoření projektu ve Visual Studiu
Nový projekt → CLR Empty Project nebo CLR Class Library. V Properties projektu ověřit: Configuration Properties → General → Common Language Runtime Support → /clr.
Struktura .vcxproj pro C++/CLI
Klíčová nastavení .vcxproj
<!-- Ve skupině PropertyGroup --> <CLRSupport>true</CLRSupport> <CharacterSet>Unicode</CharacterSet> <!-- RuntimeLibrary musí být /MD nebo /MDd (ne /MT) --> <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
Podmíněný managed / unmanaged kód
Přepínání kontextu v jednom souboru
#pragma managed ref class SpravovanaTrida { void MetodaCLR() { Console::WriteLine("CLR"); } }; #pragma unmanaged void NativniFunkce() { printf("Native\n"); // čistý C++ } #pragma managed // zbytek souboru opět managed
Při /clr nelze použít
/MT (statický CRT). Vždy /MD nebo /MDd.Typy a handles
C++/CLI zavádí nové operátory a modifikátory pro práci se spravovanou pamětí.
| Syntaxe | Popis | Analogie |
|---|---|---|
| T^ | Handle – spravovaný ukazatel (GC sleduje) | C# T t (reference type) |
| T% | Tracking reference – reference na spravovaný objekt | C# ref T |
| T* | Nativní ukazatel (nespravovaný) | C++ T* |
| gcnew | Alokace na spravované haldě (GC heap) | C# new |
| new | Nativní alokace (C++ heap, manuální delete) | C++ new |
| interior_ptr<T> | Ukazatel uvnitř spravovaného objektu | C# fixed (T* p = ...) |
| pin_ptr<T> | Fixace spravovaného objektu v paměti (zabrání GC přesunu) | C# fixed |
| safe_cast<T^> | Bezpečný přetypovací operátor pro CLR typy | C# as / (T) |
| nullptr | Nulová hodnota pro handle i nativní ukazatel | C# null |
Typy a handles – příklady
using namespace System; // Handle na spravovaný objekt String^ s = "Ahoj"; // GC spravuje String^ s2 = gcnew String("světe"); // Nativní ukazatel (manuální správa) int* nativePtr = new int(42); delete nativePtr; // nutno uvolnit // Tracking reference void Uprav(String% text) { text = "Změněno"; // modifikuje originál } // Safe cast Object^ obj = "text"; String^ str = safe_cast<String^>(obj); // Dynamic cast (vrací nullptr při selhání) String^ strOrNull = dynamic_cast<String^>(obj); if (strOrNull != nullptr) Console::WriteLine(strOrNull);
Hodnotové vs referenční typy
| Kategorie | C++/CLI syntaxe | Halda | GC |
|---|---|---|---|
| Spravovaný ref typ | ref class, ref struct | GC heap | ✅ |
| Hodnotový typ (CLR) | value class, value struct | Stack / inline | ⚠️ boxing |
| Rozhraní | interface class | — | — |
| Výčet | enum class | Stack | ❌ |
| Nativní třída | class / struct | C++ heap / stack | ❌ |
Spravovaná paměť a GC
IDisposable – destruktor a finalizer
ref class Zdroj { public: // Destruktor → IDisposable::Dispose() ~Zdroj() { Console::WriteLine("Dispose"); // uvolnit spravované zdroje } // Finalizer → Finalize() – volá GC !Zdroj() { Console::WriteLine("Finalize"); // uvolnit NATIVNÍ zdroje } }; // Automatické volání Dispose (jako C# using) { Zdroj z; // stack-like syntax → Dispose při opuštění bloku // ... } // z.Dispose() voláno zde // Alternativa: explicitní delete Zdroj^ zh = gcnew Zdroj(); delete zh; // volá Dispose, nenavrátí paměť GC ihned
GC API
using namespace System; // Vynutit GC kolekci (jen pro testování) GC::Collect(); GC::WaitForPendingFinalizers(); // Potlačit finalizer (objekt je explicitně dispose-ován) GC::SuppressFinalize(obj); // Paměťový tlak (hlásit GC nativní alokace) GC::AddMemoryPressure(1024 * 1024); GC::RemoveMemoryPressure(1024 * 1024);
Stack-sémantika (
Zdroj z; místo Zdroj^ z = gcnew Zdroj();) je preferovaný způsob – kompilátor automaticky vloží volání Dispose na konci bloku.ref class / value class
ref class – plná CLR třída
using namespace System; ref class Osoba { private: String^ _jmeno; int _vek; public: // Konstruktor Osoba(String^ jmeno, int vek) : _jmeno(jmeno), _vek(vek) {} // Property (automatická) property String^ Jmeno { String^ get() { return _jmeno; } void set(String^ v) { _jmeno = v; } } // Triviální property (auto-implementovaná) property int Vek; // Metoda virtual String^ ToString() override { return String::Format("{0}, {1} let", _jmeno, _vek); } // Statický člen static int Pocet = 0; };
value class – hodnotový typ
// Jako C# struct – kopíruje se, neřídí GC value class Bod { public: double X, Y; Bod(double x, double y) : X(x), Y(y) {} double Vzdalenost(Bod jiny) { double dx = X - jiny.X, dy = Y - jiny.Y; return Math::Sqrt(dx*dx + dy*dy); } virtual String^ ToString() override { return String::Format("({0}, {1})", X, Y); } }; // Použití Bod b1(0.0, 0.0); Bod b2(3.0, 4.0); Console::WriteLine(b1.Vzdalenost(b2)); // 5 // Boxing (value → Object^) Object^ boxed = b1; Bod unboxed = safe_cast<Bod>(boxed);
enum class (CLR enum)
enum class Smer { Sever, Jih, Vychod, Zapad }; enum class Rezim : unsigned char { A = 1, B = 2, C = 4 }; Smer s = Smer::Sever; if (s == Smer::Sever) Console::WriteLine("Na sever");
Dědičnost a rozhraní
Dědičnost
ref class Tvar { public: virtual double Plocha() = 0; // čistě virtuální virtual String^ Popis() { return "Tvar"; } }; ref class Kruh : public Tvar { public: property double Polomer; Kruh(double r) { Polomer = r; } virtual double Plocha() override { return Math::PI * Polomer * Polomer; } virtual String^ Popis() override { return "Kruh r=" + Polomer; } }; // Sealed (zapečetěná třída – nelze dědit) ref class ZapecetenaTrida sealed : Tvar { virtual double Plocha() override sealed { return 0; } };
interface class
interface class IUlozitelny { void Uloz(String^ cesta); String^ Nacti(String^ cesta); }; interface class ITiskovatelny { void Tiskni(); }; // Třída implementující více rozhraní ref class Dokument : IUlozitelny, ITiskovatelny { public: virtual void Uloz(String^ cesta) { /* ... */ } virtual String^ Nacti(String^ cesta) { return ""; } virtual void Tiskni() { /* ... */ } };
Vícenásobná dědičnost je povolena pouze přes rozhraní (
interface class), ne přes ref class. Jedna ref class může mít nejvýše jednoho spravovaného předka.Properties a události
Typy properties
ref class Priklad { private: int _hodnota; public: // 1. Triviální (auto-implemented) property String^ Nazev; // backing field automaticky // 2. Plná property property int Hodnota { int get() { return _hodnota; } void set(int v) { if (v < 0) throw gcnew ArgumentOutOfRangeException(); _hodnota = v; } } // 3. Read-only property property bool JeKladna { bool get() { return _hodnota > 0; } } // 4. Indexer (default indexed property) property String^ default[int] { String^ get(int i) { return i.ToString(); } void set(int i, String^ v) { /* ... */ } } // 5. Statická property static property int GlobalniPocet; };
Události (events)
using namespace System; // Delegát pro událost delegate void ZmenaHodnotyHandler(Object^ sender, int novaHodnota); ref class Sensor { public: // Deklarace události event ZmenaHodnotyHandler^ ZmenaHodnoty; void NastavHodnotu(int v) { hodnota = v; // Vyvolat událost (zkontrolovat nullptr) ZmenaHodnoty(this, v); } private: int hodnota; }; // Přihlášení k události Sensor^ s = gcnew Sensor(); s->ZmenaHodnoty += gcnew ZmenaHodnotyHandler( [](Object^ sender, int v) { Console::WriteLine("Nová hodnota: {0}", v); } );
Generika (Generics)
Generická třída
generic<typename T> ref class Zasobnik { Collections::Generic::List<T>^ data = gcnew Collections::Generic::List<T>(); public: void Push(T item) { data->Add(item); } T Pop() { int i = data->Count - 1; T v = data[i]; data->RemoveAt(i); return v; } property int Pocet { int get() { return data->Count; } } }; // Použití Zasobnik<int>^ z = gcnew Zasobnik<int>(); z->Push(1); z->Push(2); Console::WriteLine(z->Pop()); // 2
Constraints (omezení)
generic<typename T> where T : IComparable T Maximum(T a, T b) { return (a->CompareTo(b) >= 0) ? a : b; } // where T : ref class (musí být ref typ) generic<typename T> where T : ref class, ICloneable ref class KlonovatelnyKontejner { /* ... */ }; // where T : value class generic<typename T> where T : value class void TiskniHodnotu(T val) { Console::WriteLine(val); }
C++/CLI
generic ≠ C++ template. Generika jsou runtime (CLR), templates jsou compile-time. Nelze kombinovat template<T> s managed typy v generickém kontextu.Native ↔ Managed interop
Volání nativní funkce ze spravovaného kódu
#pragma unmanaged int NativniVypocet(int a, int b) { return a * b + 42; // čistý C++ } #pragma managed void SpravovanaFunkce() { int vysledek = NativniVypocet(5, 3); // přímé volání Console::WriteLine(vysledek); }
Nativní objekt uvnitř spravované třídy
#include <memory> // Nativní třída (C++) #pragma unmanaged class NativniEngine { public: void Spust() { printf("Engine bezi\n"); } }; #pragma managed ref class SpravovanyWrapper { NativniEngine* engine; // nativní ukazatel public: SpravovanyWrapper() { engine = new NativniEngine(); } ~SpravovanyWrapper() { delete engine; engine = nullptr; } !SpravovanyWrapper() { delete engine; engine = nullptr; } void Spust() { if (engine) engine->Spust(); } };
Nikdy neuchovávejte handle (
T^) v nativním kódu bez gcroot<T^> – GC může objekt přesunout nebo uvolnit!gcroot – uložení handle v nativní třídě
#include <vcclr.h> // pro gcroot #pragma unmanaged class NativeCallback { public: // gcroot pin-uje GC handle gcroot<System::String^> message; void Tiskni() { // přechod zpět do managed – nutno #pragma managed volání } };
Marshaling řetězců
String^ ↔ std::string / wstring / char*
#include <msclr/marshal_cppstd.h> #include <msclr/marshal.h> using namespace System; using namespace msclr::interop; // String^ → std::string String^ ms = "Ahoj světe"; std::string ss = marshal_as<std::string>(ms); // String^ → std::wstring std::wstring ws = marshal_as<std::wstring>(ms); // String^ → const char* (marshal_context potřeba!) marshal_context ctx; const char* cp = ctx.marshal_as<const char*>(ms); // cp platí po dobu životnosti ctx // std::string → String^ std::string s2 = "native"; String^ ms2 = marshal_as<String^>(s2); // const char* → String^ (bez marshal_context) const char* cstr = "text"; String^ ms3 = marshal_as<String^>(cstr); // Alternativa: přímá konverze String^ ms4 = gcnew String(cstr); // char* → String^ String^ ms5 = gcnew String(L"wide"); // wchar_t* → String^
| Zdrojový typ | Cílový typ | Metoda |
|---|---|---|
| String^ | std::string | marshal_as<std::string>(s) |
| String^ | std::wstring | marshal_as<std::wstring>(s) |
| String^ | const char* | ctx.marshal_as<const char*>(s) |
| String^ | const wchar_t* | ctx.marshal_as<const wchar_t*>(s) |
| std::string | String^ | marshal_as<String^>(s) |
| std::wstring | String^ | marshal_as<String^>(s) |
| const char* | String^ | gcnew String(ptr) |
| const wchar_t* | String^ | gcnew String(wptr) |
P/Invoke vs IJW
| Mechanismus | Popis | Kdy použít |
|---|---|---|
| IJW (It Just Works) | Přímé volání nativní DLL / .lib z C++/CLI – kompilátor vloží stub automaticky | Existující C/C++ .lib nebo .dll + dostupné hlavičkové soubory |
| P/Invoke | Deklarace nativní funkce přes [DllImport] atribut – marshaling hodnot |
Win32 API, externí DLL bez .lib, i z C# |
| COM Interop | Import typové knihovny #import "lib.tlb" |
COM servery, ActiveX, Office automation |
IJW – přímé linkování
// mylib.h + mylib.lib přidány do projektu #include "mylib.h" #pragma comment(lib, "mylib.lib") #pragma managed void VyuzijNativni() { // přímé volání – žádný Declare, žádný DllImport int r = nativni_funkce_z_lib(10, 20); Console::WriteLine(r); }
P/Invoke – praktické příklady
Win32 MessageBox a GetTickCount
using namespace System::Runtime::InteropServices; [DllImport("user32.dll", CharSet=CharSet::Unicode)] extern "C" int MessageBoxW(IntPtr hWnd, String^ text, String^ caption, unsigned int type); [DllImport("kernel32.dll")] extern "C" unsigned long GetTickCount(); // Volání MessageBoxW(IntPtr::Zero, "Ahoj!", "Test", 0); unsigned long ticks = GetTickCount();
Struktura jako P/Invoke parametr
using namespace System::Runtime::InteropServices; [StructLayout(LayoutKind::Sequential)] value struct SYSTEMTIME { unsigned short wYear, wMonth, wDayOfWeek, wDay; unsigned short wHour, wMinute, wSecond, wMilliseconds; }; [DllImport("kernel32.dll")] extern "C" void GetSystemTime(SYSTEMTIME% st); SYSTEMTIME st; GetSystemTime(st); Console::WriteLine("{0}/{1}/{2}", st.wDay, st.wMonth, st.wYear);
Callback (function pointer → delegate)
using namespace System::Runtime::InteropServices; delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [DllImport("user32.dll")] extern "C" bool EnumWindows(EnumWindowsProc^ callback, IntPtr lParam); // Delegate jako callback EnumWindowsProc^ cb = gcnew EnumWindowsProc( [](IntPtr hWnd, IntPtr) -> bool { Console::WriteLine("okno: {0:X}", hWnd.ToInt64()); return true; }); EnumWindows(cb, IntPtr::Zero);
.NET kolekce a STL.NET
System::Collections::Generic
using namespace System; using namespace System::Collections::Generic; // List<T> List<int>^ list = gcnew List<int>(); list->Add(1); list->Add(2); list->Add(3); for each (int x in list) Console::Write("{0} ", x); // Dictionary<K,V> Dictionary<String^, int>^ dict = gcnew Dictionary<String^, int>(); dict["jedna"] = 1; dict["dva"] = 2; Console::WriteLine(dict["jedna"]); // 1 // Stack<T> a Queue<T> Stack<String^>^ zasobnik = gcnew Stack<String^>(); zasobnik->Push("A"); String^ top = zasobnik->Pop(); // HashSet<T> HashSet<int>^ set = gcnew HashSet<int>(); set->Add(1); set->Add(1); Console::WriteLine(set->Count); // 1
for each (CLI range-for)
// Spravované kolekce array<int>^ arr = { 10, 20, 30 }; for each (int x in arr) Console::WriteLine(x); // Nativní std::vector – použijte C++11 range-for std::vector<int> vec = {1, 2, 3}; for (int x : vec) printf("%d\n", x);
Spravovaná pole (array<T>^)
System::Array
// 1D pole array<int>^ a = gcnew array<int>(5); array<String^>^ names = { "A", "B", "C" }; // 2D pole array<int, 2>^ mat = gcnew array<int, 2>(3, 3); mat[0, 0] = 1; // Řazení, kopírování Array::Sort(a); Array::Reverse(a); Array::Copy(a, 0, b, 0, 5);
Výjimky
Zachycení CLR a nativních výjimek
using namespace System; try { // Vyvolat spravovanou výjimku throw gcnew InvalidOperationException("Chyba!"); } catch (InvalidOperationException^ ex) { Console::WriteLine("CLR chyba: {0}", ex->Message); } catch (Exception^ ex) { Console::WriteLine("Libovolná CLR: {0}", ex->Message); } catch (std::exception& e) { Console::WriteLine("Native STD: {0}", gcnew String(e.what())); } catch (...) { Console::WriteLine("Neznámá výjimka"); } finally { Console::WriteLine("Vždy se spustí"); } // Vlastní výjimka ref class MojeVyjimka : Exception { public: MojeVyjimka(String^ msg) : Exception(msg) {} MojeVyjimka(String^ msg, Exception^ inner) : Exception(msg, inner) {} };
C++/CLI umí zachytit jak CLR výjimky (
Exception^) tak nativní (std::exception&) v jednom try/catch bloku.Delegáti a lambda
Delegáti a Func / Action
using namespace System; // Vlastní delegát delegate int Operace(int a, int b); int Secti(int a, int b) { return a + b; } // Přiřazení funkce Operace^ op = gcnew Operace(Secti); Console::WriteLine(op->Invoke(3, 4)); // 7 // Lambda (C++11 syntax, native lambda) Operace^ lop = gcnew Operace( [](int a, int b) -> int { return a * b; } ); Console::WriteLine(lop(3, 4)); // 12 // System::Func a System::Action Func<int, int, int>^ f = gcnew Func<int, int, int>(Secti); Action<String^>^ print = gcnew Action<String^>( [](String^ s){ Console::WriteLine(s); } ); print("Ahoj");
Vlákna a Task Parallel Library
Thread, Monitor, lock
using namespace System::Threading; // Vytvoření vlákna Thread^ t = gcnew Thread( gcnew ThreadStart([]() { Console::WriteLine("Vlákno: {0}", Thread::CurrentThread->ManagedThreadId); })); t->Start(); t->Join(); // Mutex / Monitor Object^ lockObj = gcnew Object(); Monitor::Enter(lockObj); try { // kritická sekce } finally { Monitor::Exit(lockObj); } // Interlocked operace int pocitadlo = 0; Interlocked::Increment(pocitadlo); Interlocked::Add(pocitadlo, 5);
Task Parallel Library
using namespace System::Threading::Tasks; // Task::Run Task^ uloha = Task::Run(gcnew Action([](){ Console::WriteLine("Async práce"); })); uloha->Wait(); // Task s výsledkem Task<int>^ vypocet = Task<int>::Run( gcnew Func<int>([]() -> int { return 42; }) ); int vysledek = vypocet->Result; // Parallel::For Parallel::For(0, 100, gcnew Action<int>( [](int i) { /* ... */ } ));
Unsafe kód a fixace paměti
pin_ptr – fixace GC objektu
#include <vcclr.h> using namespace System; using namespace System::Runtime::InteropServices; // Fixace pole pro předání nativní funkci array<unsigned char>^ data = gcnew array<unsigned char>(256); pin_ptr<unsigned char> pinned = &data[0]; unsigned char* nativePtr = pinned; // bezpečný nativní přístup memset(nativePtr, 0xFF, 256); // pinned vypadne ze scope → GC může opět přesouvat objekt // Fixace String^ pro C API String^ ms = "Testovací text"; pin_ptr<const wchar_t> wPin = PtrToStringChars(ms); const wchar_t* wPtr = wPin; wprintf(L"%s\n", wPtr);
interior_ptr – ukazatel uvnitř objektu
value struct Rect { int X, Y, W, H; }; Rect r = { 1, 2, 3, 4 }; // interior_ptr sleduje pohyb GC automaticky interior_ptr<int> px = &r.X; *px = 10; // r.X = 10 // Iterace přes managed pole pomocí interior_ptr array<int>^ arr = { 1, 2, 3 }; interior_ptr<int> p = &arr[0]; for (int i = 0; i < arr->Length; ++i, ++p) Console::Write(*p);
Atributy CLR
Použití a definice atributů
using namespace System; using namespace System::Runtime::InteropServices; // Standardní atributy [Obsolete("Použijte NovouMetodu")] void StaraMetoda() {} [Serializable] ref class Data {}; [DllImport("kernel32.dll", SetLastError=true)] extern "C" bool CloseHandle(IntPtr h); [StructLayout(LayoutKind::Explicit)] value struct Union32 { [FieldOffset(0)] int AsInt; [FieldOffset(0)] float AsFloat; }; // Vlastní atribut [AttributeUsage(AttributeTargets::Class)] ref class AutorAttribute : Attribute { public: property String^ Jmeno; AutorAttribute(String^ jmeno) { Jmeno = jmeno; } }; [Autor("Pavel")] ref class MojeTrida {};
Reflexe (Reflection)
Základy reflexe
using namespace System; using namespace System::Reflection; // Získat Type Type^ t = String::typeid; // compile-time Type^ t2 = (gcnew String("x"))->GetType(); // runtime // Výpis metod array<MethodInfo^>^ methods = t->GetMethods(); for each (MethodInfo^ m in methods) Console::WriteLine(m->Name); // Dynamické vytvoření instance Object^ obj = Activator::CreateInstance(MojeTrida::typeid); // Volání metody přes reflexi MethodInfo^ mi = t->GetMethod("ToUpper"); Object^ res = mi->Invoke(gcnew String("ahoj"), nullptr); // Čtení atributů array<Object^>^ attrs = t->GetCustomAttributes(AutorAttribute::typeid, false); for each (AutorAttribute^ a in attrs) Console::WriteLine("Autor: {0}", a->Jmeno);
Rychlá reference
Klíčová slova C++/CLI
ref class spravovaná třída (heap)
value class hodnotový typ (stack)
interface classCLR rozhraní
enum class CLR výčet
gcnew alokace na GC heap
delegate typově bezpečný callback
event CLR událost
property CLR vlastnost
generic CLR generikum
for each foreach pro CLR
nullptr null pro handle i ptr
sealed zapečetění třídy/metody
abstract abstraktní třída/metoda
literal compile-time konstanta
initonly pouze v konstruktoru
Operátory a typy
T^ handle (GC ref)
T% tracking reference
T* nativní ukazatel
@var adresa managed (ref)
*handle dereference handle
pin_ptr<T> fixace v GC heap
interior_ptrptr uvnitř GC obj
safe_cast bezpečné přetypování
T::typeid Type^ objektu
gcroot<T^> handle v native třídě
Frequently used namespaces
System Console, String, Math…
System::IO File, Stream, Path
Coll::Generic List, Dict, Stack
Threading Thread, Monitor, Task
Threading::Tasks Task, Parallel
Reflection Type, MethodInfo
Runtime::Interop DllImport, Marshal
Diagnostics Debug, Trace, Process
Text StringBuilder, Encoding
Net WebClient, HttpClient
Destruktor vs Finalizer
~ClassName()→ IDisposable::Dispose
!ClassName()→ Object::Finalize
Kdy volat ~managed resources
Kdy volat !native resources only
Stack syntaxauto Dispose na konci bloku
delete obj^volá Dispose (ne free)
Marshaling – rychlé tahy
String^ → char* marshal_context
String^ → wchar_t*PtrToStringChars
char* → String^ gcnew String(ptr)
std::string↔String^marshal_as<>
byte[] → byte* pin_ptr<byte>
struct v P/Invoke[StructLayout(Sequential)]
Common pitfalls
T^ v native použij gcroot<T^>
/MT s /clr NELZE – použij /MD
native λ → delegatelze, GC drží delegate
boxing valueObject^ = val → kopie
Dědičnost jen 1 ref base class
template+managednelze – použij generic
char* lifetimemarshal_context musí žít!
Kompilační přepínače (přehled)
| Přepínač | Popis |
|---|---|
| /clr | Smíšený managed/native kód (nejčastější) |
| /clr:netcore | Cílí .NET 5/6/7/8 (ne Framework) |
| /MD /MDd | Dynamická CRT (povinná s /clr) |
| /W4 | Úroveň varování 4 (doporučeno) |
| /GR | RTTI – povoleno (výchozí s /clr) |
| /EHa | Asynchronní výjimky (SEH + C++) |
| /fp:precise | Přesná aritmetika plovoucí řádové čárky |
| /AI<dir> | Přidat cestu k assembly referencím |
| /FU<file> | Přidat using assembly (force using) |