Skip to main content
GameDev.net gamedev.net
🔒 Locked

why c++ doesnt have many features of many modern languages like c# languages?

Started by moeen k Oct 7, 2019 at 5:48 AM 22 replies 22.4k views
Original Post
moeen k
moeen k

im a c# programmer, also i want to learn c++ in advance. as i searched many different book, i couldnt find many aspects of c# in c++ in those books. features like event, delegate, property and... but we have pointers, references and templates in c++

i know c++ has many different distributions and different versions work on different platforms. but doesnt really c++ have those features?

can you introduce me a reference(book or website) that helps me understand all advanced features of c++ specally for programming DX and OpenGL?


Shaarigan
Shaarigan

No C++ doesn't has these features because they are something that is bound to the runtime and languge model. It starts with having delegates (because events are in fact just delegates).

In C++ you don't have delegates, just function pointers. The reason is simple, a function pointer points to an address in the assembly binary. The compiler will perform just a jmp instruction as same as if you call the function directly. Those functions can have and for the kind of instruction the compiler uses need different calling conventions. A static function is just a cdecl/ stdcall, this determines how the stack is created and cleaned after the call returnes. The you have member functions that have a different calling convention: thiscall. It is again some special kind of stack creation, member functions are also statically called except that there is an additional parameter on the stack, the pointer to the object you call it for. It isn't possible to interchange calling conventions to store them in the same variable because the compiler has to ensure correct stack management. This is different for C# because in C# the CLR is able to deduce the function calls at runtime, so you can store whatever you like static or member function in a delegate.

However, there are tricks in C++ using templates to create such kind of delegate too. If you are interested in how this works, I provide delegates in my common C++ source at GitHub. It works because you store two pointers in the dynamic delegate instance, an optional object that will be used for thiscall convention and a calling proxy that is a templated function. If you assign a static function, the cdecl version call guard is used, if you use a thiscall member function, the thiscall version call guard is used. This works because those template functions are static functions and just route the arguments passed to the target function you add in there. So in fact the delegate calls the call guard passing the functions arguments and the object pointer allways and then the call guard decides how to exactly execute the function pointer given in the template argument.

Properties is another topic, they are compiler convinience for function calls. If you declare a property in C#, the compiler will add hidden functions to your class that are named get and/or set. You can investigate this if you use reflection to inspect the classes functions. So you can have this in C++ too, just add a function to your class the same way the C# compiler does or more simple


inline void PropertyName(PropertyType const& type)
{
    myVariable = type; 
}
inline PropertyType PropertyName() const
{
    return myVariable; 
}

I use this practise in my code base all the time, the only difference is in calling it


//C#
MyType m = myClassInstance.PropertyName;

//C++
MyType m = myClassInstance.PropertyName();

There are of course more topics that aren't implemented in C++ right now, for example reflection. The potentially most usefull thing in C# is tricky to do in C++ because of the CLR C# runs at. The CLR knows at runtime the type and consistency of each class and so each object. This isn't possible in C++ because objects are just memory, they don't have any information about their name, fields and methods. Yes, there is the function table in an object which inherits from another class but this only contains function pointers for derived functions and virtual overrides. There are some patterns how to implement reflection in C++ but keep in mind, reflection means storing meta information for every type in your assembly, so your code will increase in size.

Some solutions generate code for more complex reflection including fields, functions and their arguments, some other are just a lookup of a typeID or type code like in C#. It is up to you here how complex your reflection support should be. I decided for a mixed version in my code base, have a typeID for every type I use in my code, regardless of if it is built-in or mine and have generated more detailed information for some chosen types I declared by my own

Sound Master
Sound Master

Microsoft brainwashed you with C#, now you belong to microsoft.

dmatter
dmatter
15 hours ago, moeen k said:

features like event, delegate, property

Yeah C++ doesn't have those in the 'core' language, mainly because they're the kind of functionality that can be provided through libraries.

For example instead of events you might use Boost Signals2.

For delegates the C++ standard library has std::function that you can use.

For properties I would just say it's something that is typically achieved by writing member functions.


15 hours ago, moeen k said:

can you introduce me a reference(book or website) that helps me understand all advanced features of c++

https://en.cppreference.com and http://www.cplusplus.com/reference are pretty good for learning about what's in the core language and what's in the standard library.

While https://www.boost.org/doc/libs/ is a large set of high-quality community libraries and one of the most influential projects in the C++ world. Several libraries from Boost have since been standardised into the C++ standard library (e.g. boost::shared_ptr became std::shared_ptr).

Sacaldur
Sacaldur

As @dmatter pointed out, some features aren't part of the language itself, but rather of the STL (or Boost). Since in other languages (C#, Java, Python, ...), the default library is such an integral part of the language, some language features can completely depend on the existance of this library. An example would be


MyStruct? value;

wich is equivalent to


System.Nullable<MyStruct> value;

and thus requires System.Nullable to be present.

The short-hand events (meaning you don't implement add or remove explicitly) rely if I'm not mistaken on a List implementation or another data structure to handle += and -= assignments. On the other hand, the explicit syntax (comparable to properties in C#) doesn't depend on any library, because basically only offers an alternate syntax for method calls to add and remove (similar to properties with get and set).

On the other hand, one complaint about properties I heard was "this way you can't differentiate between a direct member access and an (implicit) method call just by looking at the call itself". This would apply to events as well.

Sacaldur
Sacaldur
4 minutes ago, Sound Master said:

Nice downvoting, thanks again.

Well, your post wasn't helpful at all. Instead of just saying "You got brainwashed by microsoft", one should look at the advantages and disadvantages of a certain feature, and how well it could be integrated into an existing language/system.

Dawoodoz
Dawoodoz

C++ is a long term language for huge projects
If C++ somehow added all those high-level features into the standard library, there would be even more legacy to maintain until C++ has to break backward compatibility or die. If you want a fresh start, there are many competing languages with a better balance of safety, speed and readability, but their elegance comes at the cost of being forgotten when the next shiny language arrives with adaptations for the next hardware or abstract platform.

STD is already bloated and outdated
The C++ standard collections belong to a museum by having older parts optimized for single-core computers with small but fast memory that's insignificant compared to computation. Today we have huge but slow memory with fast computation and using a linked list as a default container is like a bad joke about cache misses. This obsoletes iterators and the whole system that relies on them. Third-party libraries can be changed with every new project, adapt for custom needs using operand overloads and take advantage of the latest hardware using SIMD extensions and GPU features.

Sound Master
Sound Master

What do you mean linked list cache misses ?

_Silence_
_Silence_
47 minutes ago, Dawoodoz said:

This obsoletes iterators and the whole system that relies on them.

Why do you think that iterators are obsolete ?

Few days ago, while wanting to optimize a part of a project, I removed an std::vector since I was doing many push_back in it, and replaced it with a good old C-style array. And you know what ? std::vector was far more fast.

Except if you deal with big-data (and few are here), std::vector will give you very good performances, when used well.

Sacaldur
Sacaldur
44 minutes ago, Sound Master said:

What do you mean linked list cache misses ?

The list nodes can in theory be scattered all over the memory. If that's the case, multiple successive accesses to the same list can cause more cache misses, if the distance between the nodes is to far. Arrays on the other hand are always stored in a single block of memory.

Dawoodoz
Dawoodoz
1 hour ago, _Silence_ said:

Why do you think that iterators are obsolete ?

Few days ago, while wanting to optimize a part of a project, I removed an std::vector since I was doing many push_back in it, and replaced it with a good old C-style array. And you know what ? std::vector was far more fast.

Except if you deal with big-data (and few are here), std::vector will give you very good performances, when used well.

Linked lists are obsolete because they:
* Create serial dependencies instead of using multiple memory loads at the same time.
* Fragment the cache with pointers possibly larger than the element itself.
* Create cache misses when iterating over all the elements, because each element is a heap allocation on an unspecified memory location and the cache predictor wants linear order.
* Are difficult to split into multi-threaded workers.
* Are many times slower than arrays for the common uses no matter the size.

The creator of C++ had an in depth presentation about how linked lists have become horribly slow.
https://www.youtube.com/watch?v=YQs6IC-vgmo

Iterators lost their original design purpose because of linked lists
They are an abstract interface for handling collections without knowing if it's a linked list or array under the hood. With linked lists out of the picture, you might as well specialize for the case of consistent memory blocks and make faster and safer abstraction layers. The main problem with iterators is that they are essentially just pointers, but not the modern ones that have reference counting, have zero-overhead safety features, or iterate in strides of many elements for SIMD operations.

I might be missing some favorite feature of yours, but from my perspective it's not worth the risks posed by using two raw pointers like that, the cost of porting between languages, and the learning curve for new C++ developers.

Regarding std::vector, it's possible that you compiler's implementation used calloc instead of malloc and memset, which is faster by not fetching garbage data into the cache.

Nypyren
Nypyren

One of C++'s fundamental design philosophies is that the runtime cost of things should be minimized, even if that means omitting something that makes life easier for the programmer, if there is a chance that the convenience feature could lead to unexpected runtime overhead.

I work almost exclusively on C# projects, and our team is full of programmers who don't fully understand the ramifications of the language features they're using. I see code where every single member access has been changed to use ?. even when the objects can be trivially shown to never be null at that point in execution. Every single one of those gets expanded into a runtime null comparison and potentially complex branching. I suspect that this being heavily abused is one of the reasons that C# 8.0 introduced support for non-nullable reference types, but I haven't had experience actually using that to see if it solves this abuse or not.

Of course it's always possible that programmers can misuse C++ to the same extent, but if the language has fewer convenience features then a programmer has less stuff to potentially abuse. In my opinion C++ is better off if it sticks to its fundamental strengths and doesn't sacrifice those strengths trying to appease everyone.

SeanMiddleditch
SeanMiddleditch
5 hours ago, Dawoodoz said:

Iterators lost their original design purpose because of linked lists
They are an abstract interface for handling collections without knowing if it's a linked list or array under the hood.

Definitely not the case.

There are still tons of very useful data structures other than linked lists which aren't represented as (purely) sequential elements in memory.

One exceedingly-common-in-games examples: chunked arrays. An "array" that is a list of fixed-sized chunks, where those chunks might be sequenced in another array or in (gasp) a linked list. This is a _very_ common data structure for storing _large_ "arrays" of items since it allows much more efficient handling of memory blocks/pages and mutation of contents, and it's still very cache-friendly so long as the chunks are of an appropriate size. Such a data structure can still be random-access (if the chunks are stored in an array) and hence can be efficiently std::sortd, etc.

Even in the standard library itself, types like std::deque or std::map/std::unordered_map make use of iterators for such _basic_ functionality as "debug print all the values."

There's tons and tons and tons more uses here. Database query responses. File I/O. Lazy computation or generators. Filters and transformation pipelines. etc.

There's a very valid argument to make that the incoming ranges feature could long-term supersede iterators (since the idea is that almost every pair of iterators could be replace by a range, and even most raw pointers can be replaced by std::span, for improvements both in ergonomics and safety). That's a very different discussion and has abolutely nothing to do with linked lists, though.

Sean Middleditch – Game Systems Engineer – Join my team!
Dawoodoz
Dawoodoz
15 minutes ago, SeanMiddleditch said:

Definitely not the case.

There are still tons of very useful data structures other than linked lists which aren't represented as (purely) sequential elements in memory.

One exceedingly-common-in-games examples: chunked arrays. An "array" that is a list of fixed-sized chunks, where those chunks might be sequenced in another array or in (gasp) a linked list. This is a _very_ common data structure for storing _large_ "arrays" of items since it allows much more efficient handling of memory blocks/pages and mutation of contents, and it's still very cache-friendly so long as the chunks are of an appropriate size. Such a data structure can still be random-access (if the chunks are stored in an array) and hence can be efficiently std::sortd, etc.

Even in the standard library itself, types like std::deque or std::map/std::unordered_map make use of iterators for such _basic_ functionality as "debug print all the values."

There's tons and tons and tons more uses here. Database query responses. File I/O. Lazy computation or generators. Filters and transformation pipelines. etc.

There's a very valid argument to make that the incoming ranges feature could long-term supersede iterators (since the idea is that almost every pair of iterators could be replace by a range, and even most raw pointers can be replaced by std::span, for improvements both in ergonomics and safety). That's a very different discussion and has abolutely nothing to do with linked lists, though.

I'd just map a Lambda or something like I do with octrees, but fair enough if you have a safer alternative.

Shaarigan
Shaarigan

I think the standard library is outdated too but in my opinion for reasons like code style, conventions and the heavy nesting of templates. I know that all of these was/is usefull and it can't be changed that easy to not miss backwards compatibility with old code, but nowdays I would prefer a more modern naming. I know that such legacy systems often have these problems, especially in Web; HTML 4 is still supported in all modern browsers because for not breaking the internet and also Javascript still has old features developers I talked to would like to get rid of.

But on the other side maybe it would be wise to make a break instead of forcing new features into the language and so the standard library like Lambdas (they are prohibitted where I work because they are a potential performance issue). What's wrong with writing Vector instead of vector or SharedPtr or whatever, or have a C# style generic list instead? I know everybody has his/her prefered style but as languages like C# are widely used these days, even those styles have changed since C++ was introduced for the first time and most coding guidelines prefer the title case.

I also use iterators very rarely because I got to the point that in our case, a good old for loop did the trick too. It is like IEnumerator/ IEnumerable in C#, there are use cases for it but you won't use them all the time. But I'm not strictly against iterators or think they are outdated. I even implemented one by my own for the reflection system to iterate over the list of member fields/ functions. As I mentioned, they are like their C# siblings and can be used whenever a simple index-based iteration is not possible.


15 hours ago, _Silence_ said:

replaced it with a good old C-style array. And you know what ? std::vector was far more fast

Dont' know how you used vector in your code but if you used push_back all the time, without deeper knowledge how you replaced the old code in your project with c-style array, I think it was slow because you too often allocated memory on every add of a new element !?

The beneifts of vector is that it is a growing array (anything between the normal c-style array and C#'s generic list) so on a regrow, there is much more memory allocated as you actually use. The capacity grows every time the vector runs out of memory so allocations are pretty often in the first few pushes and become less often later. But I think you already know that, do you?

If used correctly, as a static block of memory, c-style arrays should beat vector in performance

Bregma
Bregma
3 hours ago, Shaarigan said:

If used correctly, as a static block of memory, c-style arrays should beat vector in performance

If used correctly, a single-allocated vector (capacity is known at creation time, same as an array), vector will always be faster than array because optimizing the data flow of memory pointer to by degenerate pointers is hard. We had to disable the part of GCC that optimizes that because it would lose track of the fact that memory was being written to and treat the pointer (array) as read-only as it travelled through function calls, under certain edge conditions (eg. a lambda reference capture in which it was the second capture and the first capture was larger than 12 bytes -- the life of a compiler support engineer can be interesting). The compiler always has knowledge about a vector as it gets passed around, so that problem can not happen and amazing optimization opportunities can obtain.

3 hours ago, Shaarigan said:

What's wrong with writing Vector instead of vector or SharedPtr or whatever

The C++ standard library was developed a generation earlier than Microsoft's internal coding guidelines were even though of. Microsoft's style is based on the style of Apple Toolbox they copied, which in turn was based on the Pascal language. The Pascal conventions developed in Europe differed from the Unix and C conventions developed at Bell Labs in the USA in many ways, including the use of upper-case letters in identifiers and the use of non-alphanumeric characters that do not appear on localized keyboards in the respective countries (trigraphs anyone??). One is not objectively better than the other.

It's not really reasonable to dismiss something just because it's not what you're used to. The answer to the question of what's wrong with "Vector" instead of "vector" is this. Nothing, except that's not how it is. A decision was made between arbitrary choices in which one had the greater weight of tradition and consistency at the time, and trying to retrofit later social trends has no tangible benefit and great cost.

As for me, I can't stand capital case. I didn't cut my teeth on Microsoft, I learned using C, Fortran, Algol, Bourne shell, and everything on Unix and VMS all of which used all-lower-case. Capital case is too unlike writing plain English, in which I capitalize only the first word of a sentence and some proper nouns. I do not Capitalize Common Nouns (identifiers) or any Verbs (functions). Programs are lityerature intended for other readers, and forcing them to read Chaucer is undesirable.

Stephen M. Webb
Professional Free Software Developer
Green_Baron
Green_Baron

Thanks for the insight on vector optimizations over c-style arrays. I too thought until now that the latter are faster simply because i didn't take into account the work a compiler does in the background.

-------

Can't comment on the Use or Abuse of coding Styles and Guidelines, in some natural Languages Nouns, Names, Places, etc. start with a capital Letter and the Rest is lower Case.

?

a light breeze
a light breeze
6 hours ago, Shaarigan said:

What's wrong with writing Vector instead of vector or SharedPtr or whatever, or have a C# style generic list instead?

It's uglier and harder to read, and doesn't translate at all to languages that have no upper/lower case distinction, and translates only imperfectly to many languages that do?

Not trying to start an argument here, just pointing out that for any mostly subjective question like this, no matter how strongly you feel about it, there will always be people who are just as passionate on the other side. In the end, it's far more important that you have a consistent style and stick to it than what the style actually is. Well, as long as the style doesn't include camelCase anyway.

Sound Master
Sound Master

So you are saying C# is for multicore.

Does that means now multicore microcontrollerss are being released there will be a C# comiler ?, i dont think so.

Lets see what the future brings, windows sucks anyways now a days, please dont ruing my microcontrollers with your C#.

Keep your negative things and fantasy RPGs here in this site please.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.