Living without dynamic strings

If there is one thing that’s completely banned from the Molecule Engine, it’s dynamic strings. I’ve always cringed at how many string operations and string-based look-ups where done in the last codebase I’ve been working with, hence I wanted to get completely rid of them in the run-time part of the engine – no exceptions.

You don’t need dynamically sized strings when dealing with filenames. All files need to be compiled by the content pipeline anyway, so just store a hash of the filename inside your datafiles, .pak-files, big-files, whatever. No need for strings, resolve collisions at asset compile-time. Furthermore, users (artists, designers, and programmers) shouldn’t load assets/resources by using a filename, but rather a unique identifier instead – which is conveniently handled by the content pipeline in Molecule, so no strings needed there either.

You don’t need strings as identifiers – they are error-prone, it’s easy to get them wrong, and they don’t achieve anything which can’t be achieved otherwise. Use hashes, enums, or simple integers instead.

You don’t need strings for parsing files. Parse them in-place, there’s no need to dynamically allocate memory for them. I still remember situations where the XML-parser would do more than 1 million individual allocations (!) when parsing a file – not very nice, and certainly not high-performance.

On a related note, you don’t need a std::map using a std::string as a key, there are more superior data structures for that. By “more superior” I don’t mean a hash-table either, because most of the time a simple array (or something slightly more advanced) will suffice and beat the crap out of every map and/or hash-table implementation. There, I said it.

That being said, the only place I use dynamic strings in the Molecule Engine is inside the logging mechanism, where a FixedSizeString<512> on the stack is more than enough to deal with that. Again, no dynamic allocations needed.

So far, I haven’t used any hash-table, map or dynamic string in Molecule yet (and I certainly won’t), and life’s good without them, both memory- and performance-wise. I never had the urge to use them.

8 thoughts on “Living without dynamic strings

  1. How do you handle cases like game dialogue where parts of that dialogue are replaced with parts that could change at runtime – like the name of the player in an RPG. For instance, when an NPC talks to you like “Hey, XYZ, I’ve got a quest for you!”? Or things like “You have completed this mission in XX:YY time”?

    • Localizable text is an entirely different thing, maybe that wasn’t clear enough in my post – what I meant is that I never use dynamic strings/filenames/etc. for looking up assets, sorting materials by name, or similar.

      That being said, I would probably go with fixed-size strings for localizable text as well. Assuming we know the maximum allowed length of each localizable string (and we should, for TRC reasons), we can just allocate text to be displayed from a fixed-size pool (e.g. 512 or 1024 bytes should suffice), and dynamically replace parts of the string in-place. No allocation/deallocation/fragmentation hassle, and even when displaying 100 unique strings (!) in a single frame, it takes about half a meg of storage – I can live with that.

  2. I like the idea of not using dynamic strings, however even if you use hashes to “files” inside your .pak, .big, whatever you still need to reference those files first – do you recommend hardcoding such paths?

    What about an issue that our executable can be placed anywhere on disk? Also the working directory can be basically anything so relative paths don’t work?

    • Referencing the files is done with strings, paths, directories, basically with whatever you want – but at asset build-time. Hence I only have to deal with hashes during runtime.

      Using relative paths for loading .big-files has always worked for me (and our teams at previous companies), e.g. just load the .big-file “DataPS3/MainMenu.big”, and all assets therein can be accessed via a simple hash. No matter where the executable is placed on the disk, the relative directory structure has (and is) the same. Or was it something else you meant?

      • First – thanks for answering.

        What I meant is that the relative paths are relative to the current working directory. So if you run your game the obvious way (double click on an exe) the relative paths will work since the current working directory will be the executable directory.
        On the other hand if you run your game via a shortcut with a different working dir set or just from command line while in a different directory than the exe is, those relative paths won’t be valid anymore.
        How do you address this?

  3. Ah, I see.
    To be honest, I’m not too concerned about that because it’s a PC-only problem. However, from the top of my head, there’s at least two alternatives:

    1) Generate and copy a proper shortcut when installing the game. People have to use the shortcut or otherwise the game simply won’t start correctly.
    2) At startup, you could fetch the installation directory from the registry, and set that as the current working directory via the Win32 API – voila, all relative paths will work.

    • I have to say I like option #2 and although you still need to use paths for this you don’t manipulate them = WIN 🙂
      Thank you for the answers.

    • (Necro-comment) We ended doing something similar on Trillek. The first time that we try to get access to a asset file, try to search it on this places :

      1. Relative to working dir -> ./assets/XXXXXX
      2. Relative to the executable location (= $BIN_PATH) . Were depending of the OS try :
      * Windows : $BIN_PATH/assets/XXXXXXXX
      * OSX and GNU/Linux:
      1. $BIN_PATH/../assets/XXXXXX
      2. $BIN_PATH/../share/assets/XXXXXX
      2. $BIN_PATH/../share/trillek/assets/XXXXXX

      So on a OSX/Linux machine, should work with a typical installation where :
      * binary goes to /usr/bin/
      * assets goes to /usr/share/trillek/assets/
      But at same time the other typical install of :
      * Putting all inside a folder on /opt/
      * Installing on /usr/local/
      * Installing on a folder in your home.

      And on Windows, we not become dependent of the Windows registry, and can be installed perfectly on any place.

      Obviously, we store the base path of the assets, so not is necessary to follow all this steps every time, only the first time that we try to get a assets.

      Sadly, our filepath class uses internally a utf-8 std::string :…(

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.