# Is C++ too complex?

This topic is 1858 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Yeah, GCC generates that kind of code on purpose. Try to check for overflow after the fact (e.g. by checking if the value went negative), GCC will explicitly optimize out (i.e. remove) the check. The reasoning behind this is that "it's undefined behavior so we'll try to break the code so you can't rely on it", except sometimes that can backfire since that kind of bugs on purpose may not pop up in ages, depending on the situation.

It takes a bit of a sadist to come up with that method to educate programmers.

How about a warning saying something like "this conditional expression evaluates to true unless you invoke undefined behavior"? If I am compiling some old piece of code that is now broken because the compiler is now a lawyer, I would much rather get an informative message than a bug.

##### Share on other sites
From what I've heard, it wasn't a deliberate attempt to punish people who rely on undefined behavior, but just a consequence of a change to the optimizer to generate faster code. It just so happens that it looks like an attempt to piss off people who rely on undefined behavior. Fortunately, gcc offers a flag to disable this optimization: -fwrapv.

Anyways, for a less contrived example of overloading a pointer and an integer:
 ostream & operator<<(int); ostream & operator<<(const void *); // along with half a million other member overloads and some non-member overloads 

##### Share on other sites

Anyways, for a less contrived example of overloading a pointer and an integer:
 ostream & operator<<(int); ostream & operator<<(const void *); // along with half a million other member overloads and some non-member overloads 

Yeah, that one is responsible for this fun piece of code:
#include <iostream> int main() { volatile char s[] = "Hello, world!"; std::cout << s << '\n'; } 

##### Share on other sites

Interesting.
The clear separation of a declaration and an implementation is one of the things I really like with C and C++.
The header file has (should have) everything I need as a _user_ of the class, and nothing more.
Makes the intention of the class very clear, and then I can "hide" the implementation in a cpp file.

Except that the header file also contains information about private member variables and methods. So you either give the client all the details about the class, or you end up using something like pimpl, which is yet another hoop to jump through that other languages have realised provides no tangible benefit.

Also, this separation is not mandatory, if you want, you can write everything in the same file too. Just gets more messy
I really dislike how Java forces me to put everything in the same file...
Very hard to get an overview of the class without additional tools like a smart IDE or doxygen...

So? This isn't the 80's anymore. We don't have to program in vi and cc on the command line. Realistically, managing any project of a decent size without an IDE is just making work for no reason.

##### Share on other sites

[quote name='SiCrane' timestamp='1354812511' post='5007802']
Anyways, for a less contrived example of overloading a pointer and an integer:
 ostream & operator<<(int); ostream & operator<<(const void *); // along with half a million other member overloads and some non-member overloads 

Yeah, that one is responsible for this fun piece of code:
#include <iostream> int main() { volatile char s[] = "Hello, world!"; std::cout << s << '\n'; } 

[/quote]
I ran into one of these on a function call in optimised code where the compiler had optimised away the actual instance we were interested in being passed to the function, that was a fun few days to track that down.

##### Share on other sites

[quote name='SiCrane' timestamp='1354812511' post='5007802']
Anyways, for a less contrived example of overloading a pointer and an integer:
 ostream & operator<<(int); ostream & operator<<(const void *); // along with half a million other member overloads and some non-member overloads 

Yeah, that one is responsible for this fun piece of code:
#include <iostream> int main() { volatile char s[] = "Hello, world!"; std::cout << s << '\n'; } 

[/quote]
WTF is this magic? My guess is that since there's no [font=courier new,courier,monospace]volatile[/font] overload it tries to find a conversion for [font=courier new,courier,monospace]s[/font] that matches one of the overloads, which happens to be [font=courier new,courier,monospace]bool[/font] (since [font=courier new,courier,monospace]s[/font] degenerates to a [font=courier new,courier,monospace]volatile [/font][font=courier new,courier,monospace]char*[/font], and since it's not null it converts to true)? Am I even close?

##### Share on other sites

WTF is this magic? My guess is that since there's no [font=courier new,courier,monospace]volatile[/font] overload it tries to find a conversion for [font=courier new,courier,monospace]s[/font] that matches one of the overloads, which happens to be [font=courier new,courier,monospace]bool[/font] (since [font=courier new,courier,monospace]s[/font] degenerates to a [font=courier new,courier,monospace]volatile [/font][font=courier new,courier,monospace]char*[/font], and since it's not null it converts to true)? Am I even close?

Yes, that's exactly it. In case you still thought C++ didn't have dark corners.

##### Share on other sites
Yeah, that one is responsible for this fun piece of code
Oh. Oh... I'm adding [font=courier new,courier,monospace]volatile[/font] to my list of keywords that should almost never, ever be used -- I think it only appears once in my entire game engine, inside a class that is just a reinvention of a C++11 standard class, meaning it doesn't need to exist.
I'd go as far as to say that if you're using the [font=courier new,courier,monospace]volatile[/font] keyword, you've probably got a bug.

[quote name='Olof Hedman' timestamp='1354783859' post='5007683']
Interesting.
The clear separation of a declaration and an implementation is one of the things I really like with C and C++.
The header file has (should have) everything I need as a _user_ of the class, and nothing more.
Makes the intention of the class very clear, and then I can "hide" the implementation in a cpp file.
Except that the header file also contains information about private member variables and methods. So you either give the client all the details about the class, or you end up using something like pimpl, which is yet another hoop to jump through that other languages have realised provides no tangible benefit.[/quote]I'm also in the camp that gets extremely bothered by the fact that in C#/Java/etc there's no separation between declaration and implementation. I really love the fact that [font=courier new,courier,monospace].h[/font] files can just show you the what without the how.

I often use PIMPL-style idioms, where I'm writing the same thing 3 times, instead of twice, but still prefer it over C# style classes... It's not like writing lines of code is actually anyone's bottleneck or main time-sink when programming anyway.

One of the styles that I like is having [font=courier new,courier,monospace]foo.h[/font] with just the stuff the user needs to see, then [font=courier new,courier,monospace]foo_impl.h[/font] with the member variables/data layout, then [font=courier new,courier,monospace]foo.cpp[/font] with the method implementations. If you're using a [font=courier new,courier,monospace]foo[/font] you just need the first, if you're creating a [font=courier new,courier,monospace]foo[/font] you need the 2nd, or if you're working on [font=courier new,courier,monospace]foo[/font] you need the 3rd.
This isn't the 80's anymore. We don't have to program in vi and cc on the command line. Realistically, managing any project of a decent size without an IDE is just making work for no reason.[/quote]On the same point, C++ IDEs have features to automatically duplicate/sync the necessary lines between [font=courier new,courier,monospace].h[/font] and [font=courier new,courier,monospace].cpp[/font] files, if you care for that kind of thing. Edited by Hodgman

##### Share on other sites
Header file & separation isn't a problem. I like it, the lack of it is one of many reasons for hating on so many inferior languages. It's not OCD about why can't I organize things the way I want as much as I would like to have headers available when I don't have a sylladex to tell we what every function and it's parameters are. Header/code separation makes programming easier.

Many of the advanced features of the c++ language have pretty limited use. And there is the issue that these features can take a bit of time and experience with the language to understand. The problems that exist are when these features become core components that are absolutely necessary to achieve a design, and then that design happens to be a core feature of a game or game engine or a piece of code architecture. This is where it gets to the point that every programmer on the project needs to have an advanced understanding of c++ to be able to do anything. So a solution is to avoid these features and find other simpler solutions. I don't want to hate on less advanced programmers here, but the fact of the matter is that I know a lot more about the c++ language now then I did 5 years ago, and I programmed exclusively in c++ 5 years ago and never failed to build what ever it was I wanted to build. The problem is using these features is an easy and simple alternative to building tools that extend the compilation process to generate metadata about the project to achieve the same purpose - or any other complicated task that can be achieved with the advanced features.

And this is the part where it all gets messy. The features are perfectly stable and reliable, it's not necessarily a house of cards, but the code degenerates into something not even reminiscent of what I say c++ code looks like. And use of the system, or at least users using the system comfortably without doubts as to it's quality, becomes dependent upon the relevant skill level in c++ of the programmer. It's a bit like the language itself suffers from something a lot of software suffers from, bloat. And it needs a refactor or refinement to bring it back into sanity.

The cx11 I reckon is very good, if you're using the smart pointers you can bring your class design back to what it hypothetically 'should be'. No need to worry about implementing the copy constructor or assignment operator as the new features take care of this for you. A continuation down this path of introducing features to bring things back to sanity would be welcomed by me. This is just one thing though, it seems like there should be more. And no I VILL NOT USE BOOST. Don't even. Just don't.

EDIT: -9 points for saying that advanced c++ didn't resemble simple c++, and expressing a general feeling that I don't like the inconsistency. I'm adding this:
127.0.0.1 gamedev.net
to my hosts file.

Bye everybody. Edited by Kyall

##### Share on other sites

Oh. Oh... I'm adding [font=courier new,courier,monospace]volatile[/font] to my list of keywords that should almost never, ever be used -- I think it only appears once in my entire game engine, inside a class that is just a reinvention of a C++11 standard class, meaning it doesn't need to exist.
I'd go as far as to say that if you're using the [font=courier new,courier,monospace]volatile[/font] keyword, you've probably got a bug.

Well, [font=courier new,courier,monospace]volatile[/font] was designed specifically for allowing low-level hardware access without needing to turn off optimizations, so it still has its use in drivers and such. Of course outside of that there isn't really any use for it in a modern system.

Some people use it thinking it makes operations atomic, but the compiler is free to reorder anything around [font=courier new,courier,monospace]volatile[/font] accesses (it just isn't allowed to optimize out the accesses themselves and their order inside a function), so it fails miserably (if you want atomic access you have to use dedicated atomic code, the variable declarations won't do).

##### Share on other sites
Out of curiosity, is there a specific reason why a) there isn't a conversion from [font=courier new,courier,monospace]volatile char*[/font] to [font=courier new,courier,monospace]char*[/font] (or volatile anything), and b) there aren't [font=courier new,courier,monospace]volatile[/font] overloads in the standard library?

##### Share on other sites
For a, pretty much the same reason that there isn't a conversion from const char * to char *, if you cast a pointer to volatile to a pointer to non-volatile, reads and writes to that location would no longer satisfy the volatile constraints. Ex: a write to a volatile memory location might get optimized out. For b, in C++98 because there wasn't much point when the standard library didn't support any form of threading. If it makes you feel any better, C++11 did add some volatile overloads to some standard library functions.

##### Share on other sites
b) there aren't volatile overloads in the standard library?
As sik mentioned above, it's only useful for programming at a level where you're interacting with some hardware detail, not general use. So there's not much incentive in adding overloads to [font=courier new,courier,monospace]ostream[/font] that only driver-authors will use (or won't use).
IMHO, I actually prefer the style where you read memory into local variables before using it anyway:volatile char* data = .. char c = *data; cout << c;
didn't support any form of threading
I try and avoid mentioning threading and volatile in the same sentence, because too many people already think they go hand in hand. If you're using the C++11 standard library's threading primitives, you don't need volatile. You only need it when interacting with hardware designed to support multi-threaded code, such as when implementing said primitives, or if you're writing dangerous multi-threaded code that makes assumptions about the underlying hardware. Edited by Hodgman

##### Share on other sites
Whether or not you think they go hand in hand, almost all of those volatile overloads I mentioned are for the C++11 threading library (the rest being trait types).

##### Share on other sites

I try and avoid mentioning threading and volatile in the same sentence, because too many people already think they go hand in hand. If you're using the C++11 standard library's threading primitives, you don't need volatile. You only need it when interacting with hardware designed to support multi-threaded code, such as when implementing said primitives, or if you're writing dangerous multi-threaded code that makes assumptions about the underlying hardware.

I keep hearing that, and I'm hoping you (or someone) might be able to clarify for me. Is the following C++ pseudocode (assuming C++03) bad/evil/dangerous?
 // Thread function void threadFunction(..., volatile bool* keepRunning) // "..." represents parameters I'm omitting to keep this simple and keep the focus on volatile { while (*keepRunning) { // Process a pixel in our fractal } } int main() { volatile bool keepRunning = true; start_thread(threadFunction, ..., &keepRunning); start_thread(threadFunction, ..., &keepRunning); // Main thread goes on processing UI events and sets keepRunning to false if the user has hit the Cancel button } 
As far as I understand it, there isn't anything inherently dangerous with using [font=courier new,courier,monospace]volatile[/font] in this situation (though there may be without using it), since reads/writes are atomic (of course, one thread may access it while another thread is modifying it, but this would only be catastrophic on a non-atomic operation on a non-primitive data type, AFAIU) and only one thread is writing while the others are reading. I know [font=courier new,courier,monospace]volatile[/font] doesn't make anything thread safe, but does this sufficiently prevent the compiler from optimizing your code in such a way that it would break it? Instruction reordering isn't bad in this example, as it doesn't really matter exactly where keepRunning is accessed in the loop; just so long as it is actually read.

And what C++11 data types would be appropriate here? I checked here, and as far as I can tell, a mutex and condition variable is undesirable because they require locks. Maybe futures (which I don't fully understand)?

##### Share on other sites
Is the following C++ pseudocode (assuming C++03) bad/evil/dangerous? ... Instruction reordering isn't bad in this example
That's probably the most common/acceptable use of volatile -- telling the compiler that it should definitely read that boolean each iteration, instead of optimising it to a single read -- especially in cases where reordering isn't a concern.
This is only something that works in practice though, and is reliant on assumptions about your hardware. There's no requirement in C++03 that when one thread writes a value of 'true' to the boolean, that this value will ever become visible to other threads, volatile or not.
This is another hardware-specific detail, not specified by C++03.
And what C++11 data types would be appropriate here?
In your case, [font=courier new,courier,monospace]atomic[/font] and [font=courier new,courier,monospace]memory_order_relaxed[/font]. Deciding that locks are too slow though is an optimisation issue.

One thing that bugs me is that MSVC's [font=courier new,courier,monospace]volatile[/font] has acted like C++11's [font=courier new,courier,monospace]atomic[/font] (with [font=courier new,courier,monospace]memory_order_seq_cst[/font]) since VS2005 -- i.e. on x86, it uses [font=courier new,courier,monospace]cmpxchg[/font]-type instructions. This is because too many people wrote bad [font=courier new,courier,monospace]volatile[/font]-based code that should be wrong due to re-ordering issues, so Microsoft changed the meaning of [font=courier new,courier,monospace]volatile[/font] to include a full memory fence (no read/write can be reordered past a volatile read/write), to fix people's buggy code, which just encourages people to write more buggy code that will break on other C++ compilers...

With your loop, using MSVC's [font=courier new,courier,monospace]volatile[/font] or C++11's [font=courier new,courier,monospace]atomic[/font], you get one fully-fenced read per iteration. Using locks, and assuming no contention () you get a fenced read, a regular read, and a fenced write per iteration, which isn't much different. Taking contention into account though, you also might get a busy-wait with repeated fenced reads, and possibly a context-switch.
Aside from these performance differences, there's sometimes theoretical reasons to want a particular kind of non-blocking guarantee, which is a better reason to avoid locks. N.B. some kinds of lock-free systems will have worse performance than locking ones, but do so because they require the guarantee for whatever reason.
almost all of those volatile overloads I mentioned are for the C++11 threading library
Are there any valid use-cases for [font=courier new,courier,monospace]volatile[/font] in multi-threaded code, aside from ones like the above that can be replaced with [font=courier new,courier,monospace]atomic[/font]? Edited by Hodgman

##### Share on other sites
I'm not the one who put in those overloads. The standards committee put in the volatile overloads only for threading library. If you've got a problem with threading and volatile being associated by the C++ standard library, you'll have to take it up with them. I'm just reporting the fact that they are.

##### Share on other sites
[quote name='Hodgman' timestamp='1354933996' post='5008330']
Are there any valid use-cases for volatile in multi-threaded code, aside from ones like the above that can be replaced with atomic?
I'm not the one who put in those overloads.[/quote]That was a genuine bit of curiosity on my part. If the standards committee has done this, then there must be some use for it... but in my experience [font=courier new,courier,monospace]atomic[/font] solves most of them. I was hoping someone could teach me a genuine situation where we'd need [font=courier new,courier,monospace]volatile[/font] in threaded code.

##### Share on other sites

[quote name='Cornstalks' timestamp='1354931469' post='5008309']And what C++11 data types would be appropriate here?
In your case, [font=courier new,courier,monospace]atomic[/font] and [font=courier new,courier,monospace]memory_order_relaxed[/font].[/quote]

Would memory_order_acquire/memory_order_release be more appropriate? I guess you would want the two threads to agree on the order of writes.

##### Share on other sites

I'm also in the camp that gets extremely bothered by the fact that in C#/Java/etc there's no separation between declaration and implementation. I really love the fact that .h files can just show you the what without the how.

You could argue that the separation between declaration and implementation is built into the language in C# and java using interfaces, as opposed to C++s frankly demented compilation model.

I often use PIMPL-style idioms, where I'm writing the same thing 3 times, instead of twice, but still prefer it over C# style classes... It's not like writing lines of code is actually anyone's bottleneck or main time-sink when programming anyway.

No but the best code is the code you don't have to write. I'm not saying it's a huge burden, but it's just unnecessary.

A C# interface is a much cleaner expression of intent than a header file

##### Share on other sites

You could argue that the separation between declaration and implementation is built into the language in C# and java using interfaces, as opposed to C++s frankly demented compilation model.

A C# interface is a much cleaner expression of intent than a header file
If you make a pure virtual class in C++, how is that not a clean expression of intent?

##### Share on other sites
You misinterpreted ChaosEngine.

It's not about the C++ runtime model (on which the pure virtual class acts like an interface indeed), what he criticized was the source code allegedly separation between declaration (header files) and implementation (.cpp files), which IMO violates the DRY principle. More so, AFAIK the compiler and the preprocessor don't enforce this source code separation, so it's sintatically legal to write code in the header files.

##### Share on other sites
Not only is it syntactically legal to write code in the header files: Templated code has to go there.

##### Share on other sites
Yeah, totally forgot about the templates.

So one could argue that the header/source-code model fail to provide a clear separation between declaration and implementation, becoming (taking in account only this feature) ultimatelly a hindrance to the programmer, that must keep two files in sync instead of only one.