Scroll / Orx and C++, a survey for all orx users

2»

Comments

  • edited September 2012
    Hey guys, the new x2d forum is up here: http://forum.x2d.7bit.co/
    Sorry for off-topic :) I suggest we stop discussing x2d in this thread and if anyone is interested - visit the x2d forums.

    Cheers!
    Alex
  • edited September 2012
    enobayram wrote:
    Further, without checking the documentation, (and note that, the orxOBJECT documentation has no mention of this, so you need to check the documentation exhaustively to notice the relevant section of orxSTRUCTURE!), I wouldn't know how to properly handle an orxOBJECT * appropriately.

    I completely agree that there's no doc about orx internals, they're currently all focused on using orx.
    Traditional use of orx doesn't require any knowledge of orxSTRUCTURE or other orx internals.
    orxOBJECTs should be handled in 99% of the cases via the orxObject interface. You'll never ever have to call orxStructure_IncreaseCounter yourself for example, it's strictly for internal uses only.

    The only part that I should probably improve and extract from the orxStructure interface is the functions for traversing the collections. I guess I could wrap them in all the specific modules but I think it is time consuming and rather counter productive. I instead chose of allowing a much easier access to collections via Scroll.
    Beside those functions, you really should never use the orxStructure interface.
    Now you can end up having orxSTRUCTURE pointers that you'll need to pass around. In that case there are conversion macro which really are based on a cheap C RTTI knock-off. :) It's not the best syntax but unfortunately C doesn't have support for this out of the box.
    In a modern C++ library, you'd never have to worry about such low-level concerns. Everywhere that you see an orxOBJECT *, you'd see a shared_ptr<orxOBJECT>. This isn't important only when you're receiving something BTW. As we've discussed before, when I see orxDisplay_SetBitmapData, I can't know (without asking) whether this function copies my data, takes ownership of it or just references it, leaving the lifetime management to me entirely. Imagine that, before using ANY function that takes or returns a raw pointer, you have to ask about its memory management...

    That is also some missing documentation here. Ownership in orx is never transmitted. The code responsible for creating something is also the owner and responsible for deleting it. That rule is enforced through the entire code base.
    If it weren't the case, it'd be hard to keep one's sanity as you'd have to remember all the special cases, that itself is already really bad. :)

    Memory management is extremely simple so as to make it as efficient as possible. You probably already have noticed that there aren't any extraneous copies of anything made behind the scene. If you want a result that doesn't fit in the machine word, you'll have to provide the memory for it yourself.

    As ownership is never transmitted, the use of smart pointers gets less obvious and the overall philosophy is that if something isn't required then don't do it.
    This example actually brings us to another issue. Even though orxObject_GetName(MyObject); vs. MyObject->GetName(); seems trivial, if orxObject inherits from orxStructure in C++, you'd be able to say MyObject->IncreaseCounter(); -assuming you'd even need to know about the implementation of the shared semantics- and your IDE's content assist would probably inform you about this when you pressed ctrl+space.

    There's a way to do that in C too (C99 to be precise) but it's rather ugly and, again, the use of functions such as orxStructure_IncreaseCounter are strictly for orx internals, not for external use. That separation isn't obvious unfortunately, and I should try to work on that.
    Well, you can find void * in many many more places. Going back to the orxStructure_IncreaseCounter example, that function accepts a void *. Now, looking at just this, can you tell what exactly it expects? Does it expect my orxOBJECT pointer? or will I have to extract an internal pointer of my orxOBJECT using a macro?

    The orxStructure interface required a void * as using orxSTRUCTURE * would require the use of explicit conversion everywhere. That's actually how it used to be if you go back in the svn log history. :) As those functions are internals, I chose to use a void * to prevent all the explicit conversions in all the calling places. That's the only place where such a call was made though. :)

    And for info, it'll use the cheap RTTI-like system making sure you're actually passing an orxSTRUCTURE to it and no some random piece of data, asserting otherwise.
    Well, my problem with C enums is that they allow comparing apples and oranges, though what I missed is that C++ allows that as well. My mind was at Java enums (Oh my god I'm about to say something good about java) which do not allow comparing enums of different types. Now in C++11 you have strongly typed enums that bring the same functionality.

    Yep, strongly typed enums are good, but that doesn't solve the fact you can't split their declaration. I remember either C# or Obj-C actually allows that and I found it great, too bad other languages didn't take example on this.
    Symmetry is not the answer to all problems, even if you have the discipline to follow it. For example, what happens if someone else does not realize the symmetry in your code, and returns in the middle? Even worse, what if an exception is thrown? (I know using exceptions is a personal preference) I feel much more safe, when I can look at a small segment of code, and tell that it's not doing something wrong. In C, you often have to remember to clean-up very far away from where you start doing the dirty thing.

    Well, let's put everything back in context then. :)
    I'm not really speaking of symmetry inside a function, but symmetry of logic. If you create something, then you're in charge of destroying it, etc... This is a very simple precept and that makes code a lot more robust, but it does require a certain discipline from the programmer.
    As for exceptions, well it's only valid in C++, there's no exception module in C (though I've seen some emulating them using setjmp/longjmp or similar).
    I've been working for 5 different console game developpers in the last 12 years. None of them has ever been using exceptions (or RTTI outside of debug builds for that matter). I recognize their merit, but I don't think I want to pay any overhead (both in memory or cycles) for what they bring, not for game development.

    Also exceptions tend to crash rockets, I'd be careful with those! ;)
    But I really don't want to start a flamewar :), if you like C, I'm sure you're using it in a way that eliminates the problems I encounter using it. Just like I'm using C++ in a way that I never run into the problems that C++ haters love to mention. So, the important point is, we all have strong personal reasons to choose our programming environments.

    Heh no flamewar, I like exchanging points of view. :)

    I don't really dislike C++ that to be honest, (or I'd be maso to write Scroll in C++ in the first place), I'm just very cautious in this very specific context of open source game engine.
    Over the last 15 years, 80 to 90% of all the code I've written has been in C++ as it's what I do 40-50h a week during my day job.
    I've seen so many horrors, once even forcing me to hex-edit binaries of precompiled libraries to patch some method calls, that for my own personal projects I'd rather use a much simpler language where people are less likely to make huge mistakes.
    I would like to mention again, that even though I don't like using C libraries, I don't mind wrapping them. Especially in the case of Orx, which is very well designed around an OOP architecture. You just check the documentation once, wrap what you need to use before you use it, and sleep tight at night. So, even though I seem to have strong opinions against C, I really (Really) like Orx. There's so much good in it that makes the task of wrapping stuff insignificant.

    I'm really glad you like orx and I'm sure a C++ wrapper would be beneficiary to it and will probably make a lot of potential users very happy. :)
    This brings us back to our real topic, let's do the wrapping as a collective effort! :)

    I believe Alastriona started a similar task when he started wrapping orx for .NET. Unfortunately that was a long time ago and things have changed a lot since.
    Well, C++ compilers don't play along well with each other. Especially when you start doing advanced things like template-metaprogramming, there are certain cases where you need to go as far as writing preprocessor ifs for each compiler. I have some C++ programs compiled successfully with GCC on both Linux and Windows (MinGW), but I get a 100 distinct compiler errors when I try to compile it with MSVC. Have you noticed how far the boost people have gone to make sure that you can use boost on those supported compilers...

    Heh, you don't have to go that far for C++ portability issues: name mangling is defined per-compiler. So a simple function might not even be found by another C++ compiler. :)
    You're right about this in both directions. It IS fine when you work by yourself and I believe you when you say it isn't when you collaborate with C++ programmers of various skill. Though I would still prefer to set-up the architecture in C++, and let the others program purely C-like. As a small example, imagine using smart pointers all around Orx, but keeping everything else the same otherwise.

    I see what you mean, though I wouldn't use smart pointers all over the place as they come with an overhead I don't want if I don't have the need for it. :)
    About the performance issues, I haven't done anything about them yet. They're most probably about alpha-blended pixels as you've said, because I've been using them ad-libitum :) I was planning to start a topic about it once I focus on the performance aspect.

    Well don't hesitate to let us know if you don't see any obvious cause, we'll be happy to help! :)
    Cheers! :) :)

    Cheers! :)
  • edited September 2012
    godexsoft wrote:
    Furthermore, I can't agree with Iarwain that hardcore C++ is only good when you work alone and/or don't have other developers of "various skill". I think there is a good reason people invented lead developers, senior developers and other names which usually represent the length of someone's dick in software companies :) In my opinion the core/engine of any application can be any level of hardcore meta-programming as long as it exposes a simple, intuitive, yet well-documented interface for "various skill" developers to use.

    In the very specific context of high performance game programming, I disagree. You cannot expect the lead and seniors to review all the code written by everybody to make sure it's up to a given standard. It will never happen, mostly by lack of time.
    Unfortunately, that means the codebase is of varying quality and it's hard to keep track of what should be rewritten.
    I ran Source Navigator where I currently work about 2 years ago, the active codebase (currently running on PS Vita, PS3 and Direct11) is about 8M lines of code and the quality really isn't consistent.
    Even on Splinter Cell Conviction and its 5M lines of code, the problem of code quality across the codebase was huge, due to the 100+ engineers that have been coding in it over the years.

    That's even worse in the case of open source projects where you don't hire people but where you'll have to integrate contributions. It's not the case of orx as the community is pretty small, but who knows if it grows much more one day...
    Also, i never give jobs to people who i interview if they don't meet a certain level of expected knowledge. Many developers with _years_ of experience in C++ (some over 10 years in Symbian for example) don't know the basics. I wrote a blog post on this matter a while ago.

    No matter what your level of knowledge is, there are caveats you probably don't know. And that's the case for all C++ programmers that I know, no matter how good they are.
    C being more simple is less prone to this, but of course not bullet proof.

    Also, in the context of open source development, you cannot difficultly enforce a certain level of proficiency among the contributors, and that's what we talking about here. :)

    Cheers!

    PS: As a side note, I don't think "modern C++" means the exact same thing for you and me, but I think we already covered that topic to some extent. :)
    For me it's a reference to the Modern C++ Design book of Alexandrescu. Based on that criteria, Boost sure is written in modern C++, but most (if not all?) of other open source C++ work isn't.
  • edited September 2012
    Hi iarwain, thanks again for your comprehensive reply

    iarwain wrote:
    the use of functions such as orxStructure_IncreaseCounter are strictly for orx internals, not for external use.

    What is the proper way of sharing ownership of a structure in Orx? Or am I doing something wrong by needing it? I can give an example of when I needed this. In the game that I'm currently working on, the arrangement of objects on the screen aren't the same as their arrangement in the game world. This is basically because the view is not perpendicular to the game world. In summary, I need to keep my own data structure for arranging the game objects in the world, so I need to keep orxOBJECT references. The problem with simply keeping pointers is that the object that they point to can silently die. Isn't it natural at this point to wrap this pointer in my smart pointer that uses the increase/decrease reference count?
    I've seen so many horrors, once even forcing me to hex-edit binaries of precompiled libraries to patch some method calls

    I feel for you...
    I wouldn't use smart pointers all over the place as they come with an overhead

    It's funny how one moment you find yourself talking to someone saying that C++ lacks the advanced features of modern higher level languages, just for the sake of performance (well, people write 3D games in scripting languages nowadays), and the next moment you're told that smart pointers are bad for performance and somebody would rather not use them :) I guess everybody has a threshold for how much performance they can sacrifice for fancy features :)

    The lesson I got from this entire discussion is that I should probably stop struggling with orx, and do things the orx way as much as possible.

    Cheers!
  • edited September 2012
    enobayram wrote:
    Hi iarwain, thanks again for your comprehensive reply

    Hey enobayram, don't mention it, it's an interesting discussion. :)
    What is the proper way of sharing ownership of a structure in Orx?

    Well the philosophy is that you own what you create, no more, no less. If something's created by orx, you're not the owner, if you create something, you're the owner.

    The only real exception to that rule (otherwise it wouldn't be a full-fledged rule ;)) is when using orxObject_SetOwner(), which is tempering with ownership, but that sounds pretty obvious I think (similar one with spawners). ;)
    And it's only for one aspect, object destruction, that it really matters, ie. an object being deleted will also call for the deletion all owned objects, that's about it.
    Or am I doing something wrong by needing it? I can give an example of when I needed this. In the game that I'm currently working on, the arrangement of objects on the screen aren't the same as their arrangement in the game world. This is basically because the view is not perpendicular to the game world. In summary, I need to keep my own data structure for arranging the game objects in the world, so I need to keep orxOBJECT references. The problem with simply keeping pointers is that the object that they point to can silently die. Isn't it natural at this point to wrap this pointer in my smart pointer that uses the increase/decrease reference count?

    Mmh, not really, cause increasing the reference counter will prevent it from being destructed when the rightful owner is trying to do so and will be leaked, which means you'll have to handle his fate later yourself.

    Now the real question is why your orxOBJECT is getting silently deleted? That should never happen unless you've originated it from the user side (either with a direct call or via properties such as LifeTime).

    If you need to have external data to the orxOBJECT, you should link them to it via the orxObject_SetUserData.
    Unless you're using Scroll that actually already does that behind the scene and let you create C++ classes and bind them to config sections instead.

    Now if you ever need to get a reference to an orxSTRUCTURE without taking the ownership, you should use its Global Unique IDentifier (GUID) instead of using C pointer.
    If the object associated with that GUID has been deleted, you won't find it and you can react accordingly.
    Scroll also wraps the concept of GUIDs for ScrollObject, making it easier to access without having to go through the orxStructure interface. :)

    GUIDs actually pack all the data that make any orxSTRUCTURE instance unique + the reference counter for memory tightness/alignment sake. That is the type of structure, its ID within the bank that holds those structures in memory and an ID counter that makes sure one doesn't get a different object re-allocated in the same place in memory as your old object.

    They're also used for executing orxCOMMANDS either via code, via the newly added console or via timelines. When/if there's network support one day, they should be able to help synchronizing objects over the wires too. :)

    Let me know if this is unclear as I don't think there's any tutorial or clear doc about this in the wiki yet.
    I feel for you...

    That was actually pretty interesting and fun to do. Definitely not productive though. ;)
    It's funny how one moment you find yourself talking to someone saying that C++ lacks the advanced features of modern higher level languages, just for the sake of performance (well, people write 3D games in scripting languages nowadays), and the next moment you're told that smart pointers are bad for performance and somebody would rather not use them :) I guess everybody has a threshold for how much performance they can sacrifice for fancy features :)

    It's all a matter of context, isn't it? :)
    If I'm to make a simple puzzle game with not much performance requirement or very few objects to handle, I'll definitely use higher concept and tools.
    But as a coder of a low level game engine that could virtually be used on big/ambitious games or on very limited platforms, I'd always try to go for the most efficient, if possible. Did I mention orx used to run on GBA at some point? Probably some of the recent modules wouldn't work there anymore but it's easy not to use or even compile them if needed. :)
    The lesson I got from this entire discussion is that I should probably stop struggling with orx, and do things the orx way as much as possible.

    Cheers!

    Well as long as you still feel somewhat comfortable, that should be good. We tried to design the engine to be as open/generic as possible, while finding a good compromise for efficiency. You can use it as a low level library if needed or as a higher feature-rich engine. Of course, all choices come with limitations but we tried our best to go in that direction.

    A C++ wrapper would be welcomed by a lot of people I guess, Scroll could be a base for it or not, hard to say, it's impossible to please everybody and we all have our preferences.

    Someone wrote a FreeBasic wrapper a few years ago, unfortunately it was never really finished. It's too bad as I think the FreeBasic community is pretty strong and having them on-board would be great.

    That's what I really like about the current motivation around building a community-driven editor, it's made by people having different needs than mine and to suit their needs, I'm all for that even though I don't think I'll be using it a lot as I like my config files and I dislike "modern" UIs. :)

    Cheers! :)
Sign In or Register to comment.