GDNet+ Basic
  • Content count

  • Joined

  • Last visited

  1. If you pass the pointer by value, it's [in]. It doesn't matter if a transitive relation uses the pointer to change something else somewhere else at some other time. If you pass the pointer by pointer or by reference, it's [out] or [inout] if you change the pointer, otherwise it's [in] and probably bad code. Make your rule based on what you do to the pointer, not what you do to the value the pointer addresses. If you're passing a pointer to some object into a function, you can assume that object may be changed (otherwise you would pass it by value or by const reference), but the pointer itself is not changed, so it's an [in] parameter. Think of it this way: the value of the pointer is only used as input, but the fact you're passing a pointer is documentation that the object pointed to is going to get changed. I mean, you need some kind of rule, and at least this one is consistent. Automated tool to generate documentation based on syntax is never going to capture the semantics of your ideas, no matter what.
  2. Give up. Install Linux. Never look back.
  3. It is the idiomatic and de facto standard way of indicating a class is a non-instantiable virtual base class. There is nothing hacky about it.
  4. How to create a dwarf fortress like game

    I'd suggest for your first iteration you start with Python. It's very good for rapid prototyping and constant refinement, is very capable, and is fast enough that it's become the de facto language in the AI community where the sort of massive data crunching you're envisioning is the lifeblood. The learning curse is shorter, too. Once you've stabilized at the conceptual and functional level, you can start replacing bits piecemeal with C or C++ implementations for speed, because Python be like that. There are plenty of third-party libraries available for Python for choice things like graphics, sound, and using the GPU for massively parallel number crunching like you might want for handling the updates of 1000 minions while maintining framerate. Also, it's portable to any desktop and many other devices.
  5. Nested namespaces useless typing?

    Indeed. You can namespace your identifiers in C++ using extra-lingual mechanisms like a prefix. For example, my_strings, my_system_io_paths. Programmers who are using C paradigms in C++ or who come from languages that provide no built-in mechanic for namespacing like to do that. It usually works for the, until it causes a problem. You can also use the in-language namespace mechanic in C++. For example, my::strings, my::system::io::paths. The mechanic was not developed in a vacuum but in response to real-life issues encountered frequently enough in practice to justify a language feature. The primary magic available only if you're leveraging the built-in language mechanic is something called name-dependent lookup, also known as Koenig lookup. That spell gets invoked when you're trying to resolve overloads, and becomes particularly important when you're using templates. Namespaces and templates work together in lockstep in interesting and effective ways with the end result that (usually) they do what you expect and they continue to do so when your code base is in the professional production domain of millions of lines of code. The same can not really be said of using the C-style prefix namespacing and preprocessor macros. How deep should you namespace things? Well, the answer is as deep as you need but no deeper. I would argue they should match the levelling of your project as descibed in Lakos' monograph Large-Scale C++ Software Design, which sort of implies if you're in the millions of lines of code you'll probably have around 4 namespace levels, but of course your project may vary. But don't worry, you'll probably have the same number of levels even if you're using the C-style prefix method of namespacing. You'll just be doing it without the benefit of name-dependent lookup or the possibility of a using shortcut.
  6. Nested namespaces useless typing?

    You mean, is it better to use an in-language namespace mechanic or an extra-lingual one (ie. prefixes)? Consider Koenig lookup.
  7. Well, I can't see how the OpenGL stuff could be made any more simpler in that code: you need to set up and link the shader pipeline (ie. program the GPU), you have to set up what you're going to draw in the memory accessibly by the GPU, and you need to tell the CPU to tell the GPU to draw stuff. It's as simle as necessary, but no simpler. You could simplify your program by getting rid of the GLFW code, but I think you may find it more instructive in the long run to leave it in. The above code is not a lot of code. In fact, it's going to disappear into the background noise of a useful application. Learning to program means learning to read and understand stuff like that, but in larger quantities. Give it time, it will come with hard work and practice just like any else that's difficult but worthwhile.
  8. The million monkeys approach.

    The idea of thousands of contributors creating something useful. You could never, for example, have 12,000 random developers contributing to a single piece of software that runs the entire internet and most personal electronics these days. It's impossible, the source code control system alone would be a major project. Then, who would own the copyright and control all the ideas and intellectual property? There isn't even room for personal gain and self-enrichment in such a setup.
  9. The cost of making games has both gone down and gone up as the market has matured. As usual, I draw a parallel between similar genres in the entertainment industry. Block-buster releases continue to be seen in the movie industry, for example, with budgets in the multiple hundreds of million put together by teams of hundreds of artists and technicians, all the while people can churn out 'indie' home movies for free and put them up on n the internet for free distribution -- and some of them are better than typical schlock commercial box-office fodder. Fielding a professional sports team is phenomenally expensive these days and entry into a top league prohibitively expensive, but kids can use anything for a ball and kick it around an empty lot in a barrio for hours of free entertainment. Computer games is not a special exception in the entertainment industry.
  10. Let's look at what the C++ standard library does, since it was developed by experts over many years and had extreme use testing over decades in real-world scenarios. The C++ standard library offers singletons (ie. hidden variables of static storage duration accessed only by static member functions like std::locale::global()). The C++ standard library offers "global" variables (ie. visible variables of static storage duration at namespace level) and associated namespace-level functions (eg. std::cout) to operate on them. The choice of which is used is based on two criteria. The first is ease of use. The standard IO streams are used frequently, are well-known, and there's no point beating about the bush they're "global variables." Imagine if they were "singletons" instead. std::cout.get_instance() << "Hello World!" << std::cout::endl(); My guess is printf() using its hidden globals and complete type erasure would still be the only in-use method of output if that were the case. The second, and probably more important, is the strictly-specified lifetime requirements of the "global variables" in the library. They need to exist before any user code is called and can not get destroyed until after the last user code has executed. It turns out that's a little bit easier to do with 1970s-era linkage machinery if you use global variables instead of a C++ function (although not with any modern linkers).
  11. Where to start making a game in assembly.

    Yes, libSDL2 and assembly are a good combination for game development. I'd recommend using Linux because the traditional toolchain is slightly easier for assembly development (gas, ld, make, and gdb; simpler and more consistent calling conventions; plenty of Free libraries you can leverage) but you can bend your workflow to fit other tools if that's more your thing. You'll want to stay away from mixing C++ or Objective-C libraries with assembly because of the complication of name mangling. Other languages like Java, Rust, or the interpreted languages would be painful to use with assembly. Good luck and enjoy the trip.
  12. Under no circumstances should you question my obviously superior intellect.
  13. I do not work with bottom-grade programmers who would alter functionality without understanding it first. If you absolutely must blindly follow rigid ideology and make the argument to your copy assignment operator a reference, the code would look like this. VertexBuffer& VertexBuffer::operator=(VertexBuffer const& other) { VertexBuffer tmp(other); this->swap(tmp); return *this; } Interestingly, modern compilers will pessimize that alternative code because they can not take advantage of copy elision like they can with pass-by-value parameters. Your coworker's blind obedience to cargo-cult pass-by-const-reference just cost a thousand CPU cycles. Consider giving them an opportunity to pursue a more satisfying role as a node.js front-end developer where they can cause less harm. One of the big advantages of the copy-and-swap idiom is that it provides the strong exception safety guarantee, whereas the original code did not (again, trace it through). You might argue against the use of exceptions in C++ (after all, we're already speaking of a level of mindless reverence at play in your place of work) but writing the most robust code as possible is usually a win in the long run. Sure, in this trivial example you're unlikely to see an exception, but not all code is so trivial and the copy-and-swap idiom will always apply. As for adding a self-check, consider the cost of adding and maintaining an equality operator (and corresponding inequality operator) and the cost of calling it every time and compare that to how ofter you write code that performs self-assignment. I know I very very rarely write code that assigns an object to itself, but code that is never written costs less and is guaranteed bug-free. Always spending extra CPU cycles and increasing the risk in code to make a single highly unlikely edge-case more efficient seems like a lose-lose tradeoff to me. Really, other than deleting the copy assignment operator entirely or adding debug printfs, why would you ever use anything other than the two-line copy-and-swap I wrote above for all your classes?
  14. No. Trace through the operation of the code I wrote. See how the copy constructor is used to implement the assignment operator? Now, isn't it elegant?
  15. A copy assignment operator should always look like this. VertexBuffer& VertexBuffer::operator=(const VertexBuffer other) { this->swp(other); return *this; } Of course, you need to implement your swap() member function, but that should be fairly trivial. See, the magic of this is it implements the copy assignment operator in terms of the copy constructor, which is generally the expected behaviour, so you only write the meaty function once. Even your base class should implement the operator this way. Trace it through, you'll see what I mean. Also, why would you create a class hierarchy without a virtual destructor in the base class? That smells of a misuse of inheritance.