Live data from Hacker News

Why C++ for Unreal 4

forums.unrealengine.com

151–160 of 178 posts

Re: Why C++ for Unreal 4

#151
post #39
post #8

I developed two titles with the Unreal Engine and whilst initially UnrealScript seems like an advantage it very very quickly becomes problematic. My favorite being the dependency of the C++ code on the script and the script on the C++, so if you're not careful you can end up being completely unable to do a build. As much effort as they put into the IDE it would always play second fiddle to Visual Studio. When I left…

They could also use C# instead of UnrealScript on top of C++. This way you would be able to leverage the Visual Studio while getting one of the best languages. It would make Unity3d guys welcome once they need something better as well. PS: I like the BluePrint though.

I feel like .NET is a bad choice for this.

Re: Why C++ for Unreal 4

#152
From my limited experience of Unreal Script it was a pretty rough implementation of a language, it really didn't seem to provide any real benefits.

What I would have liked to see was better support for interactive development. When I was playing with it I still had the edit/save/compile/run loop to see my changes, that's not what I want. I use Common Lisp extensively and appreciate the power of a REPL, it's available in many languages now. This brings me to the next point of providing powerful reflection support so that I can easily explore the state of the application, as well as better debugging tools.

UnrealScript was just not a very good language implementation in my opinion.

So in my opinion removing it and exposing the C++ is a good thing. But not for the same reasons that most are touting, I don't want to code in C++. But now it should be a lot easier to build a reasonable Lisp on top of Unreal Engine 4, which is what I really want.

Re: Why C++ for Unreal 4

#153

Earlier quoted context omitted.

I've described this many times in the past, but here are a few things that modern C++ does nothing to protect against: * Iterator invalidation: if you destroy the contents of a container that you're iterating over, undefined behavior. This has resulted in actual security bugs in Firefox. std::vector v; v.push_back(MyObject); for (auto x : v) { v.clear(); x->whatever(); // UB } * "this" pointer invalidation: if you ca…

Right; stl sucks. So its work, but you can make ref-safe containers, even thread-safe ones. We do that; we do audio rendering with audio-chain editing on the fly, with no memory issues. It takes care, more care than other languages. But its far from unsolvable.

Of course it's possible to write correct C++ code, just like it's possible to write correct assembly code. The point is the extra care required: every piece of code needs to be very carefully authored to ensure it's correct, to avoid the myriad pitfalls.

Re: Why C++ for Unreal 4

#154

Earlier quoted context omitted.

I've described this many times in the past, but here are a few things that modern C++ does nothing to protect against: * Iterator invalidation: if you destroy the contents of a container that you're iterating over, undefined behavior. This has resulted in actual security bugs in Firefox. std::vector v; v.push_back(MyObject); for (auto x : v) { v.clear(); x->whatever(); // UB } * "this" pointer invalidation: if you ca…

Right; stl sucks. So its work, but you can make ref-safe containers, even thread-safe ones. We do that; we do audio rendering with audio-chain editing on the fly, with no memory issues. It takes care, more care than other languages. But its far from unsolvable.

And the philosophy of Rust is, what if we encoded that "care" into the language itself? That, to me, is a clear win. It is, to me, good systems language design: codifying decades of hard earned "best practices" into the language semantics itself.

Re: Why C++ for Unreal 4

#155

Earlier quoted context omitted.

> Furthermore, there's some great tools out there to help prevent things like memory leaks. Combine that with good company practice, like code reviews, and it becomes a non-issue. The security track record of applications written in C++ disagrees with you.

Selection bias much? The 500,000 C++ applications that have never blown up in somebody's face aren't discussed on Hacker News.

Maybe most C++ applications are low-value as attack targets, so no-one has bothered to find all the corner cases that make them blow up.

The fact that applications like browsers and operating systems (which are known to be high value targets) have a lot of effort & resources put into security but still have attack vectors makes the "C++ is secure" position fairly indefensible.

Re: Why C++ for Unreal 4

#156
post #150

Earlier quoted context omitted.

There's a world of difference between a web server handling each requests within ~1-30 ms and a game simulating the entire world made from thousands of entities under ~16ms. It is very possible to write games in high-level languages, but you will lose at least half the compute power of the machine by doing so. Note that unless you're writing a Gears of Wars, you don't really need such performance and productivity win…

"It is very possible to write games in high-level languages, but you will lose at least half the compute power of the machine by doing so." Sounds definitive, interesting considering that in some cases languages like OCaml outperform C++. C++ does not mystically provide good performance. Knowledge of algorithms and appropriate data structures are far more beneficial. Does the coder know how to come up with something…

I completely agree about knowledge of algorithms and data structures being far more important to programming than the choice of language.

Many languages can outperform C++ in some cases and this language it would not be my choice for a next-gen game engine either (D would be).

The thing about going native, however, is that you also control the memory layout of these data structures easily to lower cache miss rates. You can write highly performant code in all the cases where its needed. For a game engine that's very likely to be most of the sub-systems updating the game objects and interfacing with the hardware. Writing native code makes it pretty straightforward once you learn to structure your data for cache locality and prefetch the memory when you know its going to be needed. Video game engines are chock-full of use-cases for this.

I'm mostly a functional programmer now and I do love referential transparency. It's perfect to reason about the logic of our programs and has completely changed how I view software now. But the tradeoff for this is that we lose the ability to easily reason about the execution speed of our programs without deep knowledge on how it gets compiled, and is usually dependent on the compiler vendor.

For real-time applications crunching tens of thousands of objects 60 times a second running sometimes on sub-par hardware written by armies of programmers straight out of Java-School, this makes C++ a no-brainer.

Re: Why C++ for Unreal 4

#157
post #146

Earlier quoted context omitted.

I've described this many times in the past, but here are a few things that modern C++ does nothing to protect against: * Iterator invalidation: if you destroy the contents of a container that you're iterating over, undefined behavior. This has resulted in actual security bugs in Firefox. std::vector v; v.push_back(MyObject); for (auto x : v) { v.clear(); x->whatever(); // UB } * "this" pointer invalidation: if you ca…

A lot of this just looks to be lacking const correctness. If you declared most of the mutable types const (and the use cases for a non-const unique_ptr are few) you can avoid most of these issues. I think it is a valid criticism of the language that not all non-primitive types aren't implicitly const, though. But you could never implement that without colossal backwards compatibility breakage. Which I guess is fine,…

You can return out references and still get dangling pointers with const values. For example, you can return an iterator outside the scope it lives in and dereference that iterator for undefined behavior (use-after-free, possibly exploitable as above).

Besides, isn't "C++ is memory safe if you don't use mutation" (even if it were true—which it isn't) an extremely uninteresting statement? That's a very crippled subset of the language.

Re: Why C++ for Unreal 4

#158
post #112

Earlier quoted context omitted.

I was being a bit flippant when I posted, but I'm really impressed with the list. I need to look into seeing where I can help on the compiler or runtime. No exceptions is my only objection, but I know they're dubious for a systems language.

No worries, it was a good question! I would highly recommend hopping on the IRC if you'd like some more information or have a chat. The community is very active and friendly. You can see the list of channels here: http://static.rust-lang.org/doc/master/index.html#external-r... Regarding exceptions: whilst they can be be very useful, unfortunately a significant number of large, performance sensitive C++ projects outla…

> due to overhead

My understanding is that the old exception code called "SJLJ" (short for setjmp, longjmp, which is what it was) was slow. I think each try/catch required hooks, and yes, it was.

The newer compilers generate something called "DWARF"; resources on it are unfortunately scarce, but my understanding is that you don't pay anything in speed for an exception until you throw one. (You do however pay a bit of disk/memory for data about where try/catch handlers are, I think.)

> safety concerns (the semantics can become quite hairy when mixed with destructors)

I'm assuming that you shouldn't throw in a destructor.¹

This argument, to me, always needs more information attached to it, because by itself it's meaningless. Assuming the alternative is returning either the result, or an error code, you run into exactly the same semantic issues, you're just handling them manually now. Is that better, and how?

In the manual case. If I assume I have some code that returns to me an error code that I can't handle, I need to propagate that error up to a stack frame that can. Thus, I begin to manually unwind the stack, during which, I destruct things. If we're assuming destructors can throw², then I can potentially run into the problem of having two errors: now what do I do?

C++ isn't the only language here: C#, Python, and Java share the problem of "What do you do in the face of multiple exceptions requiring propagation up the stack?", though I think C++ is the only one that solves it by terminating the program. I believe C# and Python just drop the original exception, and I have no idea what Java does. Honestly, if things are that effed up, terminate doesn't sound that bad to me. In practice in C++, most destructors can't/don't throw. (Files are about the hairiest thing, since flushing a file to disk on close can fail: C++'s file classes will ignore failures there, which doesn't exactly sit well with me. You can always flush it manually before closing, but of course, if you do this during exception propagation and throw on failure, you risk termination due to two exceptions.)

Even C has this, in that if you're propagating an integer error code up the stack, and something goes wrong in a cleanup, you've got this problem. In C, you're forced to choose, of course, including the choice of "ignore the problem entirely".

That said, I'll add the answer for Rust here. (I've never used Rust, so correct me if I'm wrong. I'm going abstract away the Rust-specific types, however.) Rust, for a function returning T but that might fail, returns either Optional or a Result-ish object, which is basically (T or ErrorObject). Rust has strong typing, so if there's an error, you can't ignore it directly, because you can't get at the result. And if you try, it terminates the "task". Strong typing is the winner here. (This reminds me why I need to look into Rust.)

¹It's not illegal to do so, but since destructors get called while an exception unwinds the stack, you can potentially run into an exception causing an exception. Two exceptions in C++ result in a termination of the program.

²If we're not, then exceptions are perfectly safe.

Re: Why C++ for Unreal 4

#159

Earlier quoted context omitted.

Of course, it's impossible to speculate without seeing the codebase, but considering that Rust makes several classes of C++ bugs impossible at compile time, I'd be hard-pressed to imagine that a Rust version wouldn't be less buggy.

If the safer type system gives the devs an unwarranted sense of security, they might write less tests, be less careful in their design, or wait longer between audits and other sanity checks. If on the other hand the devs understand which classes of bugs aren't ruled out in Rust, then sure, you will end up with fewer bugs.

Rust's type system eliminates the need to test for whole classes of bugs, because they are statically checked for at compile time. This means that tests can be more focused on logic errors rather than standard book keeping. If you look at the example set by the rust repository itself (https://github.com/mozilla/rust/), it is heavily tested and every single PR (https://github.com/mozilla/rust/pulls) is reviewed before merging. This discipline definitely filters down into third party libraries.

Re: Why C++ for Unreal 4

#160
post #146

Earlier quoted context omitted.

I've described this many times in the past, but here are a few things that modern C++ does nothing to protect against: * Iterator invalidation: if you destroy the contents of a container that you're iterating over, undefined behavior. This has resulted in actual security bugs in Firefox. std::vector v; v.push_back(MyObject); for (auto x : v) { v.clear(); x->whatever(); // UB } * "this" pointer invalidation: if you ca…

A lot of this just looks to be lacking const correctness. If you declared most of the mutable types const (and the use cases for a non-const unique_ptr are few) you can avoid most of these issues. I think it is a valid criticism of the language that not all non-primitive types aren't implicitly const, though. But you could never implement that without colossal backwards compatibility breakage. Which I guess is fine,…

> If you declared most of the mutable types const (and the use cases for a non-const unique_ptr are few) you can avoid most of these issues

Mutability in Rust is perfectly safe because of the static checks built into the type system – the compiler will catch you if you screw things up.

> you could never implement that without colossal backwards compatibility breakage

I cannot express how important immutability as default is. This prevents the issues that C++ has with folks forgetting to mark things as const. There is also lint that warns when locals are unnecessarily marked as mutable, which can catch some logic errors (I say that from experience).

Also note that I said 'immutability' not 'const'. Immutability is a far stronger invariant than const, and therefore is much safer. It could also lead to better compile-time optimisations in the future. I'm sure you know this, but just in case:

- const: you can't mutate it, but others possibly can - immutable: nobody can mutate it

Post reply on HN