It so happened that as I work, I have to use C ++ / CLI intensively and, accordingly, quite often explain to beginners what it is, how it works, how to use it, and why it’s necessary. So over time, there was a desire to write an article with an overview of the language, answers to some common questions and show places where a rake can successfully go.
What is it?
When Microsoft created the .Net platform, it decided to let the programmers write under it in several already existing languages, the syntax of which made some changes - VB, C ++. It will be about the latter. More precisely, if my memory serves me, in the first edition the language was called C ++ with managed extensions. The name itself hints at the essence - here we have given you pluses with extensions and now you can develop under .Net on already known C ++, while leaving all the power of the source language.
Actually, the first version of the syntax extension was awful a little more than completely, and made the code look like an attempt to encode and send the dialogue between Zhirinovsky and Chernomyrdin into space:
//
public __gc class Class1
{
public :
// , int
int Method1(__gc int &refValue, __gc int [] managedArr);
};
* This source code was highlighted with Source Code Highlighter .
In addition, in this syntax there were no differences between the pointer to the native type and the controlled one (in both cases “*” was used), there was no keyword to designate the null pointer, and so on. This prompted Microsoft to create a new revision of the language, which will be discussed in this article.
Note: these two versions of the syntax are called, oddly enough, “old syntax” and “new syntax”, and which one can be used in the compilation settings of the project. However, when creating new assemblies, it is better to use the new syntax, since the old one is marked as obsolete and simply bad.
')
Why do you need?
1) With the help of this language, a spherical programmer in a vacuum will be able to develop a full-fledged .Net application on favorite pros. Honestly, it's hard for me to imagine this pervert, and Microsoft is clearly not conducive to this approach, even if it doesn’t do a visual component under C ++. Actually, it does the right thing, because for such purposes there are more expressive languages, the same C #. So this is a rather theoretical possibility.
2) You can call the code already written on the pluses. Indeed, since we still have all the features of regular C ++, we can create managed wrappers for existing classes on native pluses. This gives a much greater opportunity to call unmanaged code than PInvoke, which does not know how to work with classes.
3) You can write on C ++ CLI modules where performance is critical. Indeed, among the entire zoo of languages ​​under .Net C ++, it is unique in that it is possible to write code on it that directly uses memory allocation and release, work with pointers. For example:
// , .Net–
public ref Class1
{
public :
// .Net-
void Method1();
{
BYTE *buff = new BYTE[100];
//do smth
delete[] buff;
}
};
* This source code was highlighted with Source Code Highlighter .
How does it work?
Everything is very simple. When you compile C ++ / CLI code, you get an assembly that contains both MSIL code and machine commands, into which the lines written in “clean” pluses have become. “But what about cross-platform?” - you ask, and you will be absolutely right. No In particular, this means that it will not be possible to build the same binary for 32 and 64 bit versions (collect everything under “Any CPU”). Such is the price paid for using all the features of C ++. Naturally, this refers to the case when using a mix of managed and unmanaged code. There are several compilation options:
• / clr - support for managed and native code using the new syntax.
• / clr: pure - native code is not supported. However, you can use unsafe code, as much as you can, for example, in C # assemblies using the unsafe directive.
• / clr: safe - Managed safe code only. Analogue - compiling C # assemblies without using unsafe.
• / clr: oldSyntax - analogue / clr, only the old syntax is used.
What does it look like?
Here are examples of comparing basic constructs for C # and C ++ / CLI.
Class declaration
C #:
public sealed class Class1 : Class2
* This source code was highlighted with Source Code Highlighter .
C ++ / CLI:
public ref class Class1 sealed : Class2
* This source code was highlighted with Source Code Highlighter .
Structure declaration
C #:
public struct Class1 : IEquatable
* This source code was highlighted with Source Code Highlighter .
C ++ / CLI:
public value class Class1 : IEquatable
* This source code was highlighted with Source Code Highlighter .
Interface declaration
C #:
public interface ISomeInterface
* This source code was highlighted with Source Code Highlighter .
C ++ / CLI:
public interface class ISomeInterface
* This source code was highlighted with Source Code Highlighter .
Listing announcement
C #:
public enum Enum1
{
Val1,
Val2
}
* This source code was highlighted with Source Code Highlighter .
C ++ / CLI:
public enum class Enum1
{
Val1,
Val2
}
* This source code was highlighted with Source Code Highlighter .
Creating a managed object
C #:
object obj = new object ();
* This source code was highlighted with Source Code Highlighter .
C ++ / CLI:
Object ^obj = gcnew Object();
* This source code was highlighted with Source Code Highlighter .
In C ++ / CLI, “^” is used instead of “*” to refer to references to managed objects. It is very convenient to distinguish between objects that then need to be deleted and those that are not needed. Also, when creating a local reference object, you can use the stack semantics:
Object obj ();
This makes sense either when using objects that implement IDisposable (this will be discussed later) or for value types. Note that in terms of storing and using value types of C ++ / CLI, it gives more freedom than C #, because the programmer can choose to use the reference or value. Thus, it is quite possible in some situations to save on the number of boxing / unboxing operations.
Creating a managed array
C #:
object [] arr = new object [100];
* This source code was highlighted with Source Code Highlighter .
C ++ / CLI:
array<Object ^> ^arr = gcnew array<Object ^>();
* This source code was highlighted with Source Code Highlighter .
Unmanaged arrays are created as usual.
Passing parameters to a method
C #:
void Method( int byValue, ref int byRef, out int outValue);
* This source code was highlighted with Source Code Highlighter .
C ++ / CLI:
void Method( int byValue, int %byRef, [ out ] int %outValue);
* This source code was highlighted with Source Code Highlighter .
As can be seen from this example, if “^” is an analogue of “*” from ordinary C ++, then “%” is an analogue of “&”. Moreover, the analogy is very accurate and can be traced not only when passing parameters, but also when receiving a link for example:
void Method(ValueType val)
{
ValueType ^ ref = %val;
}
* This source code was highlighted with Source Code Highlighter .
Method override
C #:
override void Method();
* This source code was highlighted with Source Code Highlighter .
C ++ / CLI
virtual void Method() override ;
* This source code was highlighted with Source Code Highlighter .
IDisposable template implementation
WITH#:
class Class1 : Disposable
{
public void Dispose()
{
this .Dispose( true );
GC.SuppressFinalize( this );
}
protected virtual void Dispose( bool disposing)
{
if (disposing)
{
//release managed resources
}
//release unmanaged resources
}
~Class1()
{
this .Dispose( false );
}
}
* This source code was highlighted with Source Code Highlighter .
C ++ / CLI:
ref class Class1
{
public :
// Dispose
~Class1()
{
//release managed resources
//call finalizer
this ->!Class1();
}
//
!Class1()
{
//release unmanaged resources
}
}
* This source code was highlighted with Source Code Highlighter .
In C ++ / CLI, the compiler implements part of the Disposable template for us, and this is apparently different from the typical implementation of the interface. Moreover, the very same compiler will not allow IDisposable to be implemented directly. However, when you get used to this, you realize that this behavior is quite logical - eliminates the need to write a bunch of duplicate code. Also, in their striving to make the release of resources similar to the usual C ++, the language creators went even further, and you can make an explicit call to Dispose in two ways:
obj->~Class1();
* This source code was highlighted with Source Code Highlighter .
and
delete obj;
* This source code was highlighted with Source Code Highlighter .
You can also use stack semantics to ensure cleanup of resources:
C ++ / CLI:
{
A();
Class1 obj;
B();
}
* This source code was highlighted with Source Code Highlighter .
corresponds to
C #:
{
A();
using (Class1 obj = new Class1())
{
B();
}
}
* This source code was highlighted with Source Code Highlighter .
What is behind the scenes?
It is clear that it was not possible to put everything into one article. The following questions were not considered:
• Syntax of delegates, properties, extension methods, foreach, etc.
• Juggling from managed to unmanaged and backward objects, arrays and other
• What is supported and not from what is in C # and in normal C ++
• Features compiling applications with assemblies C ++ / CLI
• Performance issues. When and how can I get a win? Where can you suddenly lose?
What to read?
1. Comparison of C # and C ++ syntax:
www.cyberforum.ru/cpp-cli/thread83662.html2. Basic specific language constructs:
msdn.microsoft.com/en-us/library/xey702bw.aspx3. Migration to C ++ / CLI:
msdn.microsoft.com/en-us/library/ms235289.aspxConclusion
Among all other languages ​​under .Net C ++ / CLI is rather specific. It hardly makes sense to develop a standard .Net application on it, but as communication with existing C ++ code or for optimization tasks, it’s the most.