• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
Kyall

Is C++ too complex?

122 posts in this topic

[quote name='Sik_the_hedgehog' timestamp='1354810386' post='5007788']
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.
[/quote]

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.
1

Share this post


Link to post
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:
[code]
ostream & operator<<(int);
ostream & operator<<(const void *);
// along with half a million other member overloads and some non-member overloads
[/code]
0

Share this post


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

Yeah, that one is responsible for this fun piece of code:
[code]#include <iostream>

int main() {
volatile char s[] = "Hello, world!";
std::cout << s << '\n';
}
[/code]

At least recent versions of g++ give you a reasonable warning about this one.
0

Share this post


Link to post
Share on other sites
[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.
[/quote]
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 name='Olof Hedman' timestamp='1354783859' post='5007683']
Also, this separation is not mandatory, if you want, you can write everything in the same file too. Just gets more messy [img]http://public.gamedev.net//public/style_emoticons/default/tongue.png[/img]
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...
[/quote]

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.
0

Share this post


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

Yeah, that one is responsible for this fun piece of code:
[code]#include <iostream>

int main() {
volatile char s[] = "Hello, world!";
std::cout << s << '\n';
}
[/code]

At least recent versions of g++ give you a reasonable warning about this one.
[/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.
0

Share this post


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

Yeah, that one is responsible for this fun piece of code:
[code]#include <iostream>

int main() {
volatile char s[] = "Hello, world!";
std::cout << s << '\n';
}
[/code]

At least recent versions of g++ give you a reasonable warning about this one.
[/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?
1

Share this post


Link to post
Share on other sites
[quote name='Álvaro' timestamp='1354817717' post='5007836']Yeah, that one is responsible for this fun piece of code[/quote]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='ChaosEngine' timestamp='1354824875' post='5007857']
[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.
[/quote]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.
[quote]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
2

Share this post


Link to post
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
-2

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1354848125' post='5007971']
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]
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).
2

Share this post


Link to post
Share on other sites
Out of curiosity, is there a specific reason [i]why[/i] 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?
0

Share this post


Link to post
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.
2

Share this post


Link to post
Share on other sites
[quote name='Cornstalks' timestamp='1354904661' post='5008186']b) there aren't volatile overloads in the standard library?[/quote]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 ([i]or won't use[/i]).
IMHO, I actually prefer the style where you read memory into local variables before using it anyway:[code]volatile char* data = ..
char c = *data;
cout << c;[/code][quote name='SiCrane' timestamp='1354907922' post='5008198']didn't support any form of threading[/quote]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
2

Share this post


Link to post
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).
1

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1354927245' post='5008287']
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.
[/quote]
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?
[code]
// 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
}
[/code]
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? [url="http://en.cppreference.com/w/cpp/thread"]I checked here[/url], 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)?
0

Share this post


Link to post
Share on other sites
[quote name='Cornstalks' timestamp='1354931469' post='5008309']Is the following C++ pseudocode (assuming C++03) bad/evil/dangerous? ... Instruction reordering isn't bad in this example[/quote]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.[quote name='Cornstalks' timestamp='1354931469' post='5008309']since reads/writes are atomic[/quote]This is another hardware-specific detail, not specified by C++03.[quote name='Cornstalks' timestamp='1354931469' post='5008309']And what C++11 data types would be appropriate here?[/quote]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] ([i]with [font=courier new,courier,monospace]memory_order_seq_cst[/font][/i]) 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 ([i]no read/write can be reordered past a volatile read/write[/i]), 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 ([img]http://public.gamedev.net//public/style_emoticons/default/wink.png[/img]) 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 [url="http://en.wikipedia.org/wiki/Non-blocking_algorithm#Lock-freedom"]non-blocking guarantee[/url], 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.
[quote name='SiCrane' timestamp='1354930313' post='5008307']almost all of those volatile overloads I mentioned are for the C++11 threading library[/quote]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
2

Share this post


Link to post
Share on other sites
I'm not the one who put in those overloads. [i]The standards committee[/i] 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.
1

Share this post


Link to post
Share on other sites
[quote name='SiCrane' timestamp='1354937233' post='5008346'][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?
[/quote]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.
1

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1354933996' post='5008330']
[quote name='Cornstalks' timestamp='1354931469' post='5008309']And what C++11 data types would be appropriate here?[/quote]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.
0

Share this post


Link to post
Share on other sites
[quote name='Hodgman' timestamp='1354848125' post='5007971']
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.
[/quote]

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.

[quote name='Hodgman' timestamp='1354848125' post='5007971']
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.
[/quote]

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
0

Share this post


Link to post
Share on other sites
[quote name='ChaosEngine' timestamp='1355084075' post='5008867']
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
[/quote]If you make a pure virtual class in C++, how is that not a clean expression of intent?
0

Share this post


Link to post
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.
1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0