This post will deal with the options /clr, /clr:pure,
and /clr:safe. There is also the
option /clr:oldSyntax which is
used for compiling code that uses the managed C++ extension .
The managed C++ extension is a predecessor to C++/CLI and uses a completely
different syntax.
clr:safe
This option requires that only verifiably type safe code is included in the component. This means that the code cannot access unsafe
arrays, that does not carry out boundary checks, or any unsafe pointers. Any calls to native
code must therefore be marshaled and the code cannot contain any instances of
native types.
1: #pragma once
2: using namespace System;
3: namespace ClrSafe {
4: public ref class ClrSafeClass
5: {
6: private:
7: double offset;
8: public:
9: ClrSafeClass(double offsetParam) : offset(offsetParam) {}
10: ~ClrSafeClass() {}
11: double Translate(double position) { return position + offset; }
12: };
13: }
The code above does not contain any references to native
code and can hence be compiled with the /clr:safe
option. In comparison to C# this would be similar to compile with the Any CPU flag, this is because the code does not reference any specific architecture.
clr:pure
The option like the /clr:safe
option generate only IL (Intermediate Language) output. The difference is that
native types and classes are allowed to exist within the component. The compiler makes it so that the calls to native calls are transformed to IL. There are some
limitations to this, such as that the native components cannot be declared to
export their methods from the DLL. Neither __delcspec(dllexport)
nor .def files will export native calls, this is due to that all exported methods are internally declared with __clrcall.
1: #pragma once
2: #include <cmath>
3: #include <iostream>
4: #include <msclr\marshal_cppstd.h>
5: using namespace System;
6: using namespace msclr::interop;
7: namespace ClrPure {
8: public ref class PureClass
9: {
10: private:
11: double exponent;
12: public:
13: PureClass(double exponentParam) : exponent(exponentParam) {
14: std::string mess = "Using exponential";
15: Console::WriteLine(marshal_as<String^>(mess));
16: }
17: ~PureClass() {}
18: double Power(double base) { return pow(base, exponent); }
19: };
20: }
The above code can be compiled with /clr:pure and the
Power method, on row 18, that uses a native function in the cmath library generates the following IL code:
1: .method public hidebysig instance float64
2: Power(float64 base) cil managed
3: {
4: // Code size 15 (0xf)
5: .maxstack 2
6: .locals ([0] float64 V_0)
7: IL_0000: ldarg.1
8: IL_0001: ldarg.0
9: IL_0002: ldfld float64 ClrPure.PureClass::exponent
10: IL_0007: call float64 modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl) pow(float64,
11: float64)
12: IL_000c: stloc.0
13: IL_000d: ldloc.0
14: IL_000e: ret
15: } // end of method PureClass::Power
On row 10 there is a call to the native method expressed in IL: Code compiled with /clr:pure
or /clr:safe have native entry
points and are slightly more effective since they both produce a none mixed
assembly.
clr
The /clr
option allows for complete mixing of unmanaged and managed types. This type of
assembly has a mixture of IL and native code. The entry point of the DLL is
native code that then loads the CLR when it is needed.
1: #pragma once
2: using namespace System;
3: namespace Clr {
4: class __declspec(dllexport) NativeClass
5: {
6: private:
7: double scaleFactor;
8: public:
9: NativeClass(double scaleFactor) : scaleFactor(scaleFactor) {}
10: virtual ~NativeClass() {}
11: double scale(double factor) { return factor*scaleFactor; }
12: };
13: public ref class ClrClass {
14: private:
15: NativeClass* nativeClass;
16: public:
17: ClrClass(double scaleFactor) { nativeClass = new NativeClass(scaleFactor); }
18: ~ClrClass() { delete nativeClass; }
19: double Scale(double factor) { return nativeClass->scale(factor); }
20: };
21: }
The above code has both the possibility to be called by a managed
or unmanaged component, where the managed class is merely a wrapper for the
native class.
This concludes the C++/CLI compiler options that can be used
to create mixed modes and managed DLL’s. A MSDN article on the compiler options can be found here.