npressfetimg-1466.png

C++ tutorials – General and Gameplay Programming – GameDev.net

C++ Tutorials

JoeJ said:
Yes, but my claim is those are high goals which can’t be enforced to work just by following OO paradigms, SOLID, or whatever.

Who has claimed the opposite in this thread?

JoeJ said:
There always are failures, and if so, the enforcement becomes an obstacle.

Please give me an example, because I have never encountered one, even in 10+ years of working on legacy C++ codebases. Again, what are you doing that being able to hack private members is somehow more important than the benefits private members get you in the first place?

JoeJ said:
So i’d like to break the private constraint, in cases i am willing to accepting the consequences, which include making my hacks hard to maintain and them being eventually just temporary solutions. I mean, C was meant to do anything. We can cast one type to another, even if surely incompatible. We can shoot our own foot if we desire.

Unless you’re off on your own doing lone wolf hobby stuff, you work on a team, and your coworkers probably aren’t willing to accept those consequences or maintain the hack. Certainly I would not be. Every time I have seen someone think they needed that, they actually didn’t.

JoeJ said:
Why the hell should private / public be an exception from this tradition?

Because C++ is not C and has a different “tradition.” C++’s tradition is zero-runtime cost abstractions + using the type system to prevent errors and enforce resource lifetimes. C and C++ should not be conflated at a cultural level, nor should you approach C++ with the mindset you do with C.

JoeJ said:
This is like saying, suddenly modern programmers must be protected from themselves, because they tend to do everything wrong and are just stupid.

You say this in the same post where you conflate OOP with deep inheritance hierarchies. šŸ˜‰

It’s not that modern programmers are ā€œstupidā€, it’s that unless there is a counter-pressure against it, they tend to do the smallest amount of work and write the least amount of code they can get away with to solve the exact problem they are facing right now. Frequently this is a good thing, because shipping code is good and overengineering is not; sometimes the programmer’s metaphorical myopia leads to tech debt, bugs, and code that is difficult to change because some user has yanked its internals out into its interface.

And protecting a programmer from themselves isn’t quite it, either – as I said, programmers work in teams. When a programmer hacks something and it breaks later, if they aren’t working alone it’s not them alone who will be hit by the consequences. If you’re on a big enough team, it won’t even just be the programmers; maybe something breaks in production and now your players are, too – and maybe it’s bad enough even that your game’s community managers are getting death threats. Even if it breaks before ship, there’s still going to be time wasted by producers, QA testers, etc. in finding and recording and scheduling the bugā€¦ How many hours or even days a month from now will be wasted by a shortcut taken today?

There’s even more to it, though. If you see that a type is specified to behave in a certain way, you will form certain expectations of how code that uses that type will behave that inform your understanding of what the code does and what could possibly go wrong. If there’s one or two places where a type’s design contract is subverted, the contract provided by the type system (and possibly API documentation) is no longer guaranteed and the fallout may impact the expectations that unrelated code has. Suddenly you can’t trust that type to actually behave as specified anymore, when you’re reading code that uses it. In this way, the team’s cognitive burden has increased; they have to be more careful, read the code even more closely than usual, because you have introduced a possibility that the code does not do what it says it does. That negates a big part of the benefit of using the type system to enforce design contracts in the first place!

If you don’t want to use the type system to enforce design contracts (which is your choice, though I do not endorse it), then you will not be comfortable with the direction C++ has evolved.

JoeJ said:
Well, i could add ā€˜#define private publicā€™ to my preprocessor defintions

Actually you kinda can’t, because that’s undefined behaviour. The compiler is – if I recall correctly – allowed to order private members separately from public ones. I’ve never seen a compiler do it, so you might just get away with it, but that doesn’t mean you should roll the dice on this. Whenever you invoke undefined behaviour and get away with it, you are at the mercy of the compiler author not to change things. I have had to fix far more bugs that resulted from UB than I have solved with UB, including some that were only uncovered by updating the compiler or targeting a newer C++ standard.

I’ve seen this come up with shipped video games and modding tools, too. One game I worked on had a scripting community post-launch that was exploiting a buffer overrun to get some data. They were getting lucky for years, because two arrays happened to be placed beside each other in memory and the one with the lower address was a constant size for those years. And then we went and add some new stuff to the game and that array grew in sizeā€¦ I had to write a shim in the function they were calling from script to fake the array being its original size, because ā€œdon’t break userspaceā€ was an important principle. All that could have been avoided if the original authors had just put in a bloody bounds checkā€¦

JoeJ said:
Exactly, but you can not always convince the author to do so.

Then you use different code code. Breaking the design contract in user code means the guarantees the author provided through the type system no longer apply. There may be very good reasons that the author will not do so. What makes your use case more important than whatever those reasons are?

Again, I’m interested in seeing an example where this would be justified.

JoeJ said:
But OOP does not give us that. We get no flexible graphs – we get restrictive and cumbersome hierarchies and trees.

Bad OOP, probably written in the ’90s when people were new to it and thought they needed to use every OO language feature for code to be OO. Good OO doesn’t have inflexible inheritance hierarchies. I’d argue that good OO doesn’t even have inheritance hierarchies, usually; inheritance beyond a single level is probably a mistake in most cases.

Let’s not blame a tool for the ignorance of its wielders. It might be that you, in your particular work environment, cannot trust your coworkers to apply OO tools in a helpful manner, but that is a your situation thing and if that’s the case then you ought to acknowledge that, rather than generalize.

Source: https://www.gamedev.net/forums/topic/712970-c-tutorials/5450799/