While I’m writing a series on working with SFML and C++, I thought I’d share some quick dirty tips for working with SFML that I’ve experienced lately. Some of these emerged while branching out to other development contexts that I’m normally not entrenched in – so you’ll forgive me if they seem axiomatic to you – and others simply failed to make the transition from mind to paper (or screen in this case).
Getting Started with SFML and Visual Studio
It’s evident after seeing some posts on the SFML forums that people don’t RTFM. TL;DR isn’t a thing to worry about here, so be sure to check this page (linked below) out. Visual Studio doesn’t require counter-intuitive thought concerning environment configurations – a compiler is a compiler – but the way one configures the compiler is measurably convoluted, especially if you’re used to programming in the Linux world. These steps are also valid if you’re considering creating a DLL to leverage shared code.
SFML on Visual Studio – https://www.sfml-dev.org/tutorials/2.5/start-vc.php
Speaking of creating DLLs, there’s a nasty little caveat with the default Windows header file. Evidently the versions of the min and max functions implemented in it are grossly incompatible with the ones in STL. While not a SFML issue per-se, it’s important to be aware of because it’ll likely creep in when you least expect it, and trying to determine what the root cause is from the output of the compiler is going to require several witchdoctors and an irrefutable, globally-accepted proof of String Theory. The red herring for this typically comes in the form of error C2589: ‘(‘ illegal token on right side of ‘::’ (a.k.a. The go f-yourself error).
The fix for this is the NOMINMAX preprocessor directive. You can either add it as a file-level define at the head of the file, or you can use the Project Properties dialog and add it to All Configurations and All Platforms by navigating to C/C++->Preprocessor, and appending the NOMINMAX option to the Preprocessor Definitions field. If ever you come back to this dialog to ensure that the value was set, you’ll need to drill-down into each configuration and platform to see that the value was applied.
Deleted Copy Constructors, sf::NonCopyable
A core component of a game engine that I wrote has an Asset Manager that’s very similar to the one used in MonoGame, except that it doesn’t use the Pipeline concept. Assets are loaded into memory via PhysicsFS, and they’re translated into SFML Asset Constructs that are stored in a STL Container, specifically std::unordered_map. Some SFML Asset Constructs, specifically sf::Music, inherit from classes that leverage sf::Thread, and, of crucial note, sf::Thread inherits from sf::NonCopyable. While this utility class doesn’t explicitly delete the copy constructor and assignment operators, it marks them as private. Children of this class will likely, if you’re using C++11 or greater, have these functions implicitly deleted since they’re not valid. In the absence of STL Containers, this isn’t too much of an issue, especially since attempts at copies or assignments would result from explicit statements that you yourself wrote. When STL Containers are around and you encounter an error from implicitly deleted function calls, we’ve traipsed into another arena where compiler output is infamously horrid to the degree of being near useless.
To give some concrete to my exposition, the offending statement was this:
... typedef sf::Music sfmusic; typedef std::unordered_map < std::string, sfmusic > ab_bgm; ...
std::unordered_map leverages std::pair to join the key to the value, and while I haven’t been able to dissect the issue deeper than this, it would appear that std::pair is likely subsuming the lifecycle of the objects it contains. Because there is no copy constructor or assignment operator for an object that inherits from sf::Thread, and because std::pair is attempting to leverage either one of those functions in some way, the latter is going to throw up in the most flamboyant of ways.
Although what’s next is likely not a representation of a clean or efficient way to mitigate this, I’ve found that it works. For starters, the declaration changes slightly:
... typedef sf::Music sfmusic; typedef std::unordered_map < std::string, sfmusic* > ab_bgm; ...
Next, the member function of the Asset Manager that is responsible for copying the asset data from raw bytes into live SFML Asset Constructs takes an extra step of manually allocating the memory for it before using the sf::Music copyFromMemory function:
... case targetloader::bgm: bgmb [file] = new sfmusic (); bgmb [file]->openFromMemory (d, f.length ()); ...
Of course, because we’re now wandering down the path of explicit memory allocations, we’ve got to be responsible for cleaning it up, so the intermediate destructor does some work to delete allocations in this bank, if there were any, before removing the bank itself.