Live data from Hacker News

Show HN: Crown – A flexible game engine written from scratch in C++

github.com

91–100 of 179 posts

Re: Show HN: Crown – A flexible game engine written from scratch in C++

#91
post #73
post #56

Earlier quoted context omitted.

In general if you're not using exceptions, you're not going to be using features that haven't actually been published in a formal standard (optional). This now means that you can't use any constructors, so how do you have Containers of foo?

> you're not going to be using features that haven't actually been published in a formal standard (optional). So you then have things like: class Foo { public: static Foo* create(); ... }; ... Foo* foo = Foo::create() if ( foo != nullptr ) ... > so how do you have Containers of foo? std::vector Not saying either of those are better than the alternative (I prefer using exceptions and RAII), just pointing out what I've…

I don't think this is the proposal. The proposal is that the object contains a genuine constructor that only does the bare-bones "safe" stuff, and then it has a separate non-static method that does the might-fail initialization. So:

  class Foo
  {
  public:
    Foo();
    bool initialize();  // returns success
    ...
  };
  ...
  Foo foo;
  if ( !foo->initialize() ) { // handle error }
This also means you can break up your initialization so that you drive the risky pieces from outside the object, rather than monolithically from within.

This has a further benefit for testing, since you can use your major objects without fully initializing the entire world that they depend on.

Re: Show HN: Crown – A flexible game engine written from scratch in C++

#92

While I find the "sane C++" approach pragmatic and practical all things considered, I'm firmly in the "time to use a better language if possible" camp. The problem with approaches requiring extra discipline is: it's an extra mental burden to bear while programming. Also, you'll always be limited by the fact that you're working in a less pure ecosystem and will likely end up using libraries written in "not very sane C…

The first step would be to actually define such a subset. And what happens when this subset calls into "full" C++? If you don't want to lose interoperability you have to be pretty conservative with the things you disallow.

In particular since C++ kept the C-style preprocessor "just copy and paste that file in there" includes it would be tricky to handle "sane C++" including "full C++", especially since templated C++ tends to put a massive amount of code in the headers (think boost for instance, which is mostly .hpp "headers"). You'd have to tell the compiler to switch the "sane" flag on and off within the same translation unit depending on the original source of the code. Nothing impossible, but not exactly elegant. Alternatively you could use a new `extern "sane-C++" { ... }`-type block around all your code to tell the compiler what to do.

At any rate you'll have to make sure that your sane subset can always inter-operate with the "wild" C++ without any cross-contamination.

But really I think the main problem is that you'd have trouble getting a consensus on what would be your sane subset. Some devs will tell you to get rid of exceptions altogether, others will tell you that multiple inheritance is the work of the devil. Some will want to ban raw pointers (or at least severely gimp them). And some will want none of that but something else instead.

Re: Show HN: Crown – A flexible game engine written from scratch in C++

#93

Earlier quoted context omitted.

Generally speaking many C++ game engines avoid the STL stuff and reimplement their own more predictable containers, often with custom allocation schemes. The engine at the last game company i worked at, for example, had its own containers and memory allocator and allowed you to define the allocation category and pool per allocator and per object class (so, e.g., dynamic strings would be isolated to their own pool to…

I felt the same way dipping my toes in C++ for a few years. C99 is definitely my preferred language. But when in Rome...

I'm kind of curious about doing this more often, but it's the lack of clean collections that puts me off.

What do you do regarding collections? (Dynamic arrays, hashmaps)?

Re: Show HN: Crown – A flexible game engine written from scratch in C++

#94
post #2

Interesting! I'd be interested in better understanding the motivation behind Orthodox C++. In particular, you seem to dump most of the C++ standard library: "Don't use anything from STL that allocates memory, unless you don't care about memory management." I now mostly avoid templatization in my own code unless there's a really good reason. But the standard library often lets me avoid explicit memory allocation. Woul…

Generally speaking many C++ game engines avoid the STL stuff and reimplement their own more predictable containers, often with custom allocation schemes. The engine at the last game company i worked at, for example, had its own containers and memory allocator and allowed you to define the allocation category and pool per allocator and per object class (so, e.g., dynamic strings would be isolated to their own pool to…

> Generally speaking many C++ game engines avoid the STL stuff and reimplement their own more predictable containers

This seems a little like cargo cultism. I wonder if any of these shops regularly measure the performance of their custom containers and compare with the standard library on a modern optimizing compiler and make a reasoned judgment that it's still currently worth the trade-offs to stick with their own stuff.

Re: Show HN: Crown – A flexible game engine written from scratch in C++

#95
post #83

If you come to finance, most of the developers love premature templatization, it makes them feel like they know something. Not sure it that can be attributed to their insecurity about C++ coding skills, but it gets really ridiculous at times.

What's wrong with using templates vigorously?

Some things that are not so nice about templates:

- dozens to hundreds of compiler error lines for a single error, where it's hard to find out what the real problem is (IDEs often point to the wrong line)

- Code is hard to follow. E.g. try to figure out from boost asio source code which code is actually used if if you do a async_read(socket). I personally gave up after the second level of template substitutions, and have only a chance to follow the execution path in the debugger.

- Besides goto definition also other IDE features do not work really well with templates. E.g. no autocompletions for constructors with make_shared.

Re: Show HN: Crown – A flexible game engine written from scratch in C++

#96
post #68
post #51

Earlier quoted context omitted.

How do you deal with constructors that might fail?

You don't have constructors that 'fail'.

So, allocating memory for objects is not part of construction? Again, why not just stick with C?

Re: Show HN: Crown – A flexible game engine written from scratch in C++

#98
post #88
post #69

Earlier quoted context omitted.

Disclaimer: Not a go apologist, I admire it, but don't use it. With Go you get a compiler error if you don't do something with that error. You have to explicitly decide to ignore it with `_`. As far as I remember that's quite different from C where you can get an error code, ignore it, and never realize you've missed it.

Not quite. Try this: import "os" func main() { os.Open("this file does not exist") } This will compile just fine, producing no compiler error or warnings whatsoever. The error is just silently ignored. Compare to Rust: use std::fs::File; fn main() { File::open("this file does not exist"); } This will produce the following warning: warning: unused result which must be used --> test.rs:14:5 | 14 | File::open("this file…

A slightly related note: you can get similar behavior in C (and C++) with compiler extensions. In GCC and clang marking function with '__attribute__((warn_unused_result))' will produce a warning if function is called without using the result. Equivalent for MSVC is '_Check_return_'.

Obviously this is not nearly as convenient as your Rust example, but enables some of its the benefits.

Re: Show HN: Crown – A flexible game engine written from scratch in C++

#99
post #73

Earlier quoted context omitted.

> you're not going to be using features that haven't actually been published in a formal standard (optional). So you then have things like: class Foo { public: static Foo* create(); ... }; ... Foo* foo = Foo::create() if ( foo != nullptr ) ... > so how do you have Containers of foo? std::vector Not saying either of those are better than the alternative (I prefer using exceptions and RAII), just pointing out what I've…

I don't think this is the proposal. The proposal is that the object contains a genuine constructor that only does the bare-bones "safe" stuff, and then it has a separate non-static method that does the might-fail initialization. So: class Foo { public: Foo(); bool initialize(); // returns success ... }; ... Foo foo; if ( !foo->initialize() ) { // handle error } This also means you can break up your initialization so…

It was almost the exact proposal specified by my grandparent post except using a pointer rather than an optional.

It's also a technique that is widely used. See for example the cocos2d-x game library.

The benefit of such a technique is that you can then make the constructor private, making it impossible to create an object and not also call the initialize() method.

Re: Show HN: Crown – A flexible game engine written from scratch in C++

#100
post #56
post #33

Earlier quoted context omitted.

Instead of using a constructor you can use a constructor method e.g.: class Foo { public: static std::optional create(); private: Foo(); };

In general if you're not using exceptions, you're not going to be using features that haven't actually been published in a formal standard (optional). This now means that you can't use any constructors, so how do you have Containers of foo?

My bad, I thought std::optional is part of C++14, it seems to be part of the next standard C++17, but there's still boost::optional.

About the container issue: if you have objects that might fail during the creation it seems like a bad idea to allow things like:

    std::vector foos(10);
Having a separate initialization method which might fail - like proposed by others - is another option, but this means your objects need some kind of internal initialization state, and whenever you're handling such an object you never can be absolute sure that it's in a valid state.

I'm quite a big fan of making invalid state not representable in an object and handling failure cases as early as possible.

What the create method returns depends heavily on your use case. If the returned objects can always be allocated on the heap, then a pointer or unique_ptr can be returned.

Post reply on HN