Everyone knows that there is nothing more stupid than arguing "which language is better." For example, better for what? Different languages are successful in different niches - and it is pointless to draw definitive conclusions without considering this.
But what happens if we turn to experienced specialists who themselves understand all this and ask them to arrange the C ++ vs C # holivar? It turns out that you can find out a lot of interesting details. The word "cross-platform" can be applied in both ways to both languages, but what does this mean in practice? Is C ++ actively developing now? Has C # ever broken backward compatibility? The answers may be obvious to those who are already deeply immersed in both languages at once, but there are few such people - and everyone else will learn something new.
From C ++,
Sergey sermp Platonov , Chairman of the Program Committee of the
C ++ Russia Conference, participated. The C # side was represented by
Anatoly Kulakov - he is included in the PC of the
DotNext conference and among the leaders of
DotNetRu . And the leader of the discussion, in the life of which both of these worlds coexist, was
Dmitry mezastel Nesteruk .
Dmitry: Good afternoon, colleagues. Welcome to informal gatherings on the topic of programming languages. On the Internet, we are constantly reminded that languages cannot be compared. And today we will do exactly what you can’t do: compare C ++ with C # and .NET, their pros and cons. Please introduce yourself.
Anatoly: My name is Anatoly, and today I will drown for C #, because I have been studying this language from its first versions and, it seems, I know everything about it.
Sergey: Hi, my name is Sergey, today I will sink for C ++. Dima correctly said that we will compare the pros and cons. Everyone calls it “Pros”, it’s known that, it turns out that C # in this discussion will be a minus. Is that right, Anatoly?
Anatoly: C # has two more pluses! Therefore, I think that this is an evolutionary development of the advantages that are already obsolete and are not able to compete almost anywhere.
Education
Dmitry: I have the first topic for our discussion. Imagine that new students come to the university, they need the first language. What do you think should be the first language that people get in their first year: C ++, C #, or assembler in general?
Sergey: I taught for some time, so I have an established opinion. I understand that we are going to discuss here which language is better, and I stand for C ++ ... But to learn C ++, you need to understand the architecture of the computer. And with this, the big problem of teaching students (at least in the university where I taught). And to teach algorithms and stuff, you probably need something that does not focus on the infrastructure, in the language itself. Here Eiffel was an attempt to do this, but there is also a lot of magic. Therefore, I would say that neither of our two languages is suitable.
Programming is different, and it is not “programming” that teaches, but algorithms, data structures, and so on. It is possible that it makes sense to choose your own instrument on each subject. Understand some kind of Lisp data structures. And C ++, respectively, should be given after students understand something about architecture. And then it will be possible to understand why all this pain and suffering. I won’t even argue that the pluses are about pain.
Anatoly: Yes, I completely agree that you need to separate objects, and not put it in “programming” and hammer everything in one language. But if you get to the point where you have learned the basics, fundamentals, algorithms, and begin to choose some kind of industrial language, then here, of course, C # will be much better. Because it does not force you to learn all this dregs at the level of architectures, bytes of memory, and other “sunsets by hand”. It gives an immediately understandable language, simple syntax, and in this language from the first or second year you can earn quite tangible money.
Dmitry: There is an argument that not giving beginner students some things like pointers is some kind of sacrilege. They will have a huge hole if a person does not understand that, for example, a link is actually just the address of a variable in memory. What do you think about this?
Anatoly: 20 years ago, this was true when computers had not enough memory, not enough disks and other things. Now look at these javascripts, they drag 500 megabytes of libraries into each "hello world". How much do they take in memory? What is their performance? What are the links there? Yes, nobody cares. The main thing is to quickly roll and release something in production. I do not claim that this is a good or right way, I argue that you need to change with the realities. Maybe now it’s not so important how much your link takes.
Sergey: Probably depending on where. Dmitry, as far as I understand, was interested in algorithmic trading - I can vividly imagine how he pulls up libraries on JS to send an order to the exchange.
Dmitry: Well, yes, of course, in practice no one uses languages of this type there. Although theoretically this may be possible: let's not forget that not weak money is being thrown into the JS infrastructure. Engines that make JS compilation into anything and everything. Many consider this language as the first-class language for everything in general.
Naturally, algo trading is now a remote discipline from such a discipline, but algo trading and financial mathematics as a whole is generally a specific area. It just predominates C ++. And it predominates partly due to inertia, simply because of historical reasons: in the beginning everyone was in C ++, and this area is conservative.
Sergey: I do not agree. I now work in fintech, and colleagues who have been here from the very beginning of algorithmic trading talk about large companies that first wrote in Java. At first, Java coped with algorithmic trading, but when the market began to grow and competitors with C ++ appeared, at some point they simply couldn’t do it, they didn’t manage to do everything efficiently ... So not everyone in algorithmic trading started with C ++. Just those who did not write on it died. Such a natural selection.
Dmitry: Actually, you can take it wider. There are many examples where even large banks keep their algorithms in an Excel document. They then use Excel also as a server to calculate all this. There are hellish brakes, but it all depends on whether you are doing high-frequency trading (or generally something high-frequency). If you are a market maker - naturally, you need high performance, and there the business is not even limited to C ++, there we go into hardware and HDL languages.
But our discussion is not only around algorithmic trading, but around simple things too. Here I give an example. In connection with the construction, I needed to write several small applications calculating different things: for example, how to lay bricks around the contour of a house. And I can hardly imagine how to do such things in C ++, because everything related to the UI is weaker there. There is only one framework, Qt, and even writing on it is very difficult. And if I sit down for C #, for WinForms, then I just instantly make the application.
Anatoly: Well, the visual part has always been a strength of C #. Microsoft invested a lot in molds, and even in cross-platform molds, and in general in visualization. Therefore, if we are talking about visual desktop applications, then it seems to me that the pluses are generally far, far behind.
Sergey: Well, it depends, as always. I really do not like UI, but on the pluses I constantly have to do it. It would seem to bring JS and just interact with the pros. But I worked with embedded, and there it is hard. People bought some kind of fast expensive engine, but it still could not cope with the normal rendering of the UI written in JS. And after rewriting all this on Qt, it turned out to overclock. Ordinary story.
Cross-platform vs cross-platform
Sergey: I wanted to clarify here. I know little about C #, I touched it myself a very long time ago, in the very first versions (back then I was broken backward compatibility). So the question is: is it still being developed only by Microsoft?
Anatoly: No, now it is cross-platform, open and verified under ISO (ECMA-334 and ISO / IEC 23270). By the way, as far as I know, C ++ still does not have an open ISO specification, only paid. And C #, by contrast, is completely open. Developed by many companies (including Google, Amazon and Samsung), we have the
.NET Foundation . I don’t even know a more open language now than C # and its .NET platform.
Sergey: Well, Haskell.
Anatoly: By the way, the author of Haskell works at Microsoft Research and made a lot of efforts to bring all sorts of cool things to C # - for example, a static check, some kind of reflection, which you probably can’t even dream about.
Sergey: They can dream, and even work is going on in this direction. But it is clear that everything has its own price. In C ++, they simply refuse to pay this price.
Anatoly: Which one? They are compiled for two hours, what else could be the price?
Sergey: In C ++, the principle of zero cost abstraction. Well, that is, a virtual machine is not a zero cost abstraction, right? We have to put up with this.
Dmitry: Well, but a virtual machine can, for example, obscure code for a particular architecture. Whereas in C ++, if I use the AVX instruction on a computer without AVX, my process just shuts down. I would say that this argument is not entirely correct, because theoretically - I emphasize, theoretically - JIT can do what C ++ is not available. Namely, optimization at the time of launch.
Sergey: But in C ++ during compilation you can completely control what instructions you need. In this case, you do not control it with your hands, but you give up the instrument (compiler). Look, what instructions are on this architecture, what set of instructions ...
Dmitry: This is understandable. But we can formulate it this way: since there are a million platforms, we will never get any kind of ideal, because we cannot release a million versions with different compilation flags. Right? We usually release x86 and x64, and do not break it all down into any subgroups.
Sergey: Why can’t we? XXI Century. Hold Docker with different parameters, that's all.
Dmitry: When we have an end client who downloads our application, he wants to download a specific binary. And in this binary, the best we can do is stick everywhere if. Like "if cpuid is so-and-so and avx support is so-and-so, then we use version of algorithm 25". As a result, we need 25 different versions of the same algorithm, because acceleration depends on platforms, it is platform-dependent.
Sergey: I probably agree. It's just that, to be honest, I have never created a non-internal product. I am mainly in companies that themselves use their product.
Dmitry: Well, of course, the best option is when you predictably know architecture. In this case, strictly speaking, no one forces you to use x86 instructions at all. You can take a specific card (for example, Nvidia Tesla) and do whatever you want. This is my approach too, I control my architecture. But when you make mass-market decisions for the user ... If you take some conditional ReSharper, he cannot just take and use GPU acceleration for any arbitrary indexes. Because GPU acceleration is not a portable thing.
Sergey: Actually, there are approaches (now, you probably don’t need to go into details), there are interesting guys (the author of the approach, it seems, has now also moved to Microsoft). Here at our conference the year before last there was a
report on how to write such a program, which itself will understand what is where (relatively easy, again, zero cost abstractions). So that on the fly you can choose, and if anything, correctly rebuild the code in a CUDA-style ...
Dmitry: Actually, CUDA itself is trying to solve this problem, because in CUDA there is a certain intermediate layer of
PTX that deals with this. But this is still very difficult, because the iron is radically changing evolutionarily, and it’s very difficult to keep up with it at all. And if we look at the use of GPU acceleration, for example, in Adobe products, then they use a very narrow section of available technologies. If your card is correct - then yes, everything will be. But if it is a little exotic, nothing is guaranteed in this regard.
Anatoly: In this discussion, we touched on a rather important topic, such a myth: C ++ was declared many years ago as such a cross-platform language, but at the moment cross-platform is much more in C #. One and only binary works everywhere where .NET is supported, and this is almost everywhere.
Sergey: Well, this is also quite unfounded. As a person who spent most of my life in embedded, I rarely saw .NET be supported by the hardware manufacturer's toolchain. Companies that produce iron take the same G ++ or Clang or make it start generating code for their platform.
Dmitry: Yes, but the problem is that every time they do this, they lose something from C ++. For example, Nokia used a variation of C ++, but their C ++ was with crazy twists and crazy APIs that infuriated everyone. That is, it is not just C ++, but C ++ for a particular platform. And then the problems begin. For example, take the same CUDA. It is as if it should just let the pros through itself; it is not a compiler at all, but just a driver. But despite this, she has gags with the fact that she still uses some kind of framework for tearing CUDA files into GPU and CPU parts. And sometimes she doesn’t succeed.
Sergey: I didn’t mean that a bit. It's just that when I hear “.NET runs everywhere,” most of my working biography kicks back. When you buy a piece of hardware with a custom processor, it just comes bundled with the G ++ delivery. And there is ordinary C ++, which G ++ from the toolchain can convert to machine code supported by this particular processor.
Dmitry: But again, this must be reassembled ...
Sergey: Of course.
Dmitry: And the idea that we take an existing plus code and drag it onto a piece of iron - this idea also does not work, because suddenly you dragged your regular x86 somewhere, where you have 8 gigabytes of memory for everything about everything, and not expand: for example, there is no swap to disk, because there is no disk and access to it. This is if we are talking about portability. Depends on the goals, naturally.
Anatoly: Pros work on more devices, and, of course, embedded is one of the strongest parts. But usually you have to somehow adapt your code to the platform. This is bad. I can cover a huge number of platforms, architectures, models with one code. On the pluses, I had to think about each individual platform: where it will start there, and under what conditions. And it is very bad, it is very holding back.
Stability, compatibility, language development
Dmitry: Zero cost abstractions were also mentioned, but the problem is that this has a huge price. For example, in .NET there is a concept of an enumerated type and an IEnumerable interface. And for each type, for example, an array, you can take and go through the iterator. But in C ++ there is no such idea. Due to zero cost abstraction, to get around the collection, there are a couple of begin () and end (), there are rules for their work, and all this is much more complicated (especially for those who begin to program). This is a direct problem: how to get around some array from A to Z.
Sergey: If I understand correctly what you are talking about ... If you just need to go around some container from beginning to end, now you just write, like in some Python.
Dmitry: This is all wonderful. But you, for example, do not use polymorphism for this. You cannot say that here I have a function that receives a certain value, which is enumerated a priori. You can’t say that I have a value that implements the interface, and this interface has an iterator, for example.
Sergey: We are talking about which C ++? About C ++ in general, C ++ of the future, C ++, which is now being accepted as a standard?
Dmitry: Well, if it will be in the pros of the future ...
Sergey: In C ++ 20, this is already there. You can already say, you can even declare yourself. These are not interfaces, but, how to say it right ... In general, you can declare that your type must satisfy such and such conditions. For example, it has begin and end, which return an iterator. And an iterator is such a prepared concept in the standard library. He says what it is, describes. Iterators are also different. In general, we try, we make it more convenient for people.
Dmitry: It seems to me that this grew out of the fact that people simply realized that it’s hard to live without the concepts of iterable of an object. Because it is not clear how to write generalized things. Yes, zero cost abstraction means that we do not have the cost of walking around the v-table when searching ... In .NET there is just a specific method, for example. And we, in order to find it, naturally, have to spend efforts, which the pluses refuse. But from the point of view of usability, the end result is not so good, I would say.
Sergey: Naturally, there must be a balance. You can’t have everything at once.
Anatoly: This makes us wonder: how many years have passed. Alternative languages evolve, and in them such basic things appear from the very beginning. Now they are catching up with something more substantial and interesting. And the pluses sit for ten years with the same incomprehensible syntax, obscure abstractions, incomprehensible crutches and are poorly developed. You can put this as one of the minuses.
Sergey: Well come on! What does “poorly developed” mean?
You mentioned a committee - C ++ also has an ISO committee that develops it. There are representatives there, including Microsoft, who strongly drown for the fact that "you can’t do this, because we have a lot of legacy that we need to support." Just C ++ is the language already held. And, of course, she walks very carefully. One of the main tasks (which was already declared by Straustrup when creating it) is compatibility with C. But now C has even evolved quite far, you have to designate which C is compatible with.
And in my opinion, now C ++ is developing at a tremendous pace. Regarding concepts and so on - in fact, everything grows, of course, not from iterability. In fact, the development follows what Alexander Stepanov also described - one of the authors of what we now call "generalized programming", the person who actually dragged templates, generics, and so on into C ++. To be honest, I don’t know how much the committee is inspired by these ideas, but it seems to me that there is definitely some intersection with them.
Anatoly: It seems that all these metaclasses, iterators - this is really an inspiration, which was already many decades ago. Even if you take metaprogramming, templates, macros - all these people have long experienced, grinded, and there are much simpler, obvious, understandable concepts. In other languages, this is all done a million times better and faster, with type safety, checking at compile time, and so on.
Sergey: Wait, you’re already talking about something that not everyone is willing to pay for. I do not want my program to check something in compile time without my knowledge. Do you understand?
Anatoly: I think all of these with flags can be configured. You set the optimization level, and it either checks you or not. It's not a problem.
Sergey: Often you need to control everything with your hands. Know exactly what is going on. Because the tools - well, that.
Dmitry: It's not even about tools. Here the fact that languages like D and Rust, say, say: well, yes, there is such a thing that when you access an array element, you can check it, but you can not check it. And they just give it to the user, that is, you can say “but let's turn off the array checks”, “but let's turn it on”. That is, some kind of control in this regard.
Sergey: It is not clear when you have Unsafe and Safe, as in Rust, I do not see the difference with C, for example, in this case.
Anatoly: The difference is that you can write safely and you can write fast. And in C you have to write dangerously. Well, yes, maybe fast. Stability is sometimes more important than speed.
Dmitry: In fact, if we start digging this topic with new languages, in C ++ there are things that are generally very difficult to convey to people. A simple question: what size is int? In most languages, you know the answer to this question. You say: int is 32 bits. But you don’t know the pros. You know the size on your particular computer because you remember it, but, strictly speaking, you don’t even want to use the basic types, because they are non-deterministic. And such things infuriate me when there is a set of legacy approaches, like the int will be different on different platforms. And now we already understand that this cannot be done. Why not step further than this and somehow solve this problem?
Sergey: Well, this is decided. There are
STDs , the required types with a fixed length. Now the representative of Russia on the committee is
dragging an int of variable length (well, again, with zero cost abstraction).
Anatoly: Do I remember correctly that there is even a non-deterministic size of a pointer to a method? That is, under different compilers and different platforms, the pointers are different?
Sergey: Naturally, this is architecture. When you are close to hardware, how can you guarantee the size of the pointer, if you are on 8-bit, then on 64-bit?
Anatoly: And how can one do arithmetic on pointers after that? This is crazy.
Sergey: I mean? Well, be careful.
Anatoly: I see. The approach is clear everywhere, carefully controlling everything with handles.
Sergey: Well, yes. Again, in modern C ++ standards, approaches are developed ... If we talk about choice, then in modern pluses, in fact, there is a choice whether to use the garbage collector. It's just that GC is built there on reference counters.
In general, according to your colleagues, I, sorry, feel that you have not updated your knowledge about modern pluses for a long time.
Now, people like Straustrup, who are part of the pantheon of plus gods, come with many calls to figure out how to teach modern C ++. The problem is what people think in the 2003 C ++ categories and teach in the same categories. And in connection with this there are interesting new projects and approaches, there are modern courses - let's say the guys from Yandex have made a wonderful
course . And now in pluses it is considered a bad manners, for example, to use pure new and delete.
Dmitry: As for your comment about updating knowledge ... The nuance is that my approach, for example, is to use the small C ++ delta, which is guaranteed to work for me and with which I am “friends”. You see, C ++ is extensive. There is template metaprogramming, and everything would be fine, there is a lot of magic, but, unfortunately, this magic is unreadable. This is a code in which a non-author can’t figure it out without any special knowledge, in a sense a black box. And there are a lot of such black boxes in the pros, areas of darkness that cannot be digested ... I would like, I don’t know, your option to be calculated predictably, well and without any tricks.
The simplest example is talking about ranges (
range-v3 and this whole topic). On the one hand, all this is great: there are things that have been in C # for several years, allowing, for example, to build a calendar by any transformation of the standard collection. On the other hand, the way it is implemented in C ++ is simply unpleasant compared to C #: it is heavy, not readable.
Sergey: This is flavoring. On the contrary, I like it. As I understand it, you are to the Nibler
report and its presentation ...
Dmitry: You see, when the “or” operator is used to filter a collection, I immediately have questions about this. Both C # and Java did everything through the dot, through the usual methods.
Sergey: And it seems to me that this is inspired by Bash. That is, it is just a pipe.
Dmitry: Well, yes, probably this explains something in this approach.
Sergey: It explains a lot! Let's talk about PowerShell, since we are talking about Bash. Who saw PowerShell?
Anatoly: I write in PowerShell, a great language. But again, the pipe needs to be inserted where it is in place, where all the architecture is permeated by it. Not where you need to do one single action, and it is here idiomatically bad syntax.
Sergey: In range pipe, it’s just very ...
Dmitry: In range they are used, in my opinion, for the following reason ... I will say this: if in C ++ there were extension methods or extension functions, you would use them, of course. Because the most natural thing if you need to sort a collection is to write “collection. filter ()”. And not “collection | view :: filter () ".
Anatoly: I also got the impression that you were shot at your feet for 20 years, hit in the face, banged your head on the wall, and then finally say: “Well, now we have done everything beautifully in the 20th standard, now let's teach the pros are right. ” Yes, no one wants to teach them correctly! That is, it is a long-term pain.
Sergey: Please do not teach. What is the problem? Write in C # - trade on it, write embedded. I'm not against.
Anatoly: Well, there are narrow niches where the pluses are still there.
Sergey: Embedded is a “narrow niche” ... Right now, looking around in my kitchen, I see a bunch of computers.
Dmitry: Every time I fly by plane, I think: “Damn, I hope these pluses wrote everything well there.”
Sergey: Well, by the way, there is mainly Ada, as far as I remember.
Dmitry: Ada dominates there, yes.
Anatoly: By the way, I recently came across
an excellent article where the author in different languages (about 10) wrote a low-level driver - network driver for a 10-gigabit Intel card. From C to Swift, JS, Python and, of course, C #. If we look at these graphs, which he got, then C # on big batches (when launch costs are leveled) goes on par with C and Rust.
That is, if we are talking about performance, it may be a misconception that C # is very much inferior somewhere.
Scratched Metal , , C#- .
: . , Java, C#, , . . , , C# Java — . , , : , , - .
: 99% «» # — , . 1% - . C# . — - , , …
: , : . .NET , , System.Numerics.Vectors. , , ? , .NET, ( ). .NET (AVX ), - .
: .NET Core 3 , AVX. , , .
: , 2019 . . , - , C# . C++ . , .
: , C# , . .
: ? « »?
: 2019 , , - , , …
: , , , …
: , , enumerable' , . pattern matching.
: , . pattern matching . , .
: , , « , , , , ». , Java . Java «, , ». Java , .NET .
: , . , . — , . «zero cost abstraction» — , . «legacy», .
: , zero cost abstraction — . zero cost abstraction, .
: abstraction.
: — . , .
: , « » , , .
: . , , — .NET C# yield . , -, . async/await , . , .
: , .
: , . , 10 .
: . , , . , , . . , 80-, .
: , , C# 1.0, .
: . , .NET , .
: , API, . .
: , C#. , .
: breaking change, C# 4 — foreach -. , 1.x , , - - .
: Microsoft , , . , - .
: , .NET , C++, Java.
: , , . C++ — , , « ».
: , , ?
: , , . , . : ? Microsoft, , Google . — , stackful, stackless, . , , , .
: , , corporate interests, , — , ,
«let them eat cake» .
: . , . .
: , , , - zero cost, - . yield, - - — , , , , - .
: , , , , - .
: ,
Boost .
: , . Boost , , , … - . std::string, , . size(), length(), : , - ? - , , . , . , , , . , , , - .
Compilation
: , , , . ?
: , , «», .
: .
: embedded-, include, ?
: . embedded -.
, , - ? , , . ?
: . 150 . - , . .
: , !
: , Steam, , , 64 . , 150 ?
: , , .
: , -. ? , , , — , zero cost abstractions . -?
: , , , , ?
: , . , , .
: , , , . — , . , . , , . , . -. C.
: . «». : , . , , , . . , .
: , . . , . proposal. .
: , proposal. , « »: , STL , . , - , .
: STL . STL . , , STL — , , .
: , — , ? , greenfield. brownfield development, . — , . — . ?
: , . , , . , . , , . G++ , Clang . .
: , , , . « , A, B». , .NET, . , , , , , ?
: , , . , C++ 2.0. ++C++. , C.
: , . , , . , , , , #include #import - — . , . , , , , .
. , . , , , C# C++, .
: , , 10 . , , , , , , . « », . , .
C# , C++. , C# . , , . , , , , JIT' — , , - ( int). , , , , .
: , , , C# — . , , C++ . , . ( , ) — cutting edge. , UI- C++, , . C# — . C++ , .
, . , , , C++ , , , . , .
, C# Microsoft. , .NET Foundation, , , Microsoft. , .
C++ Russia DotNext . : ?