lundi 5 janvier 2015

What are best practices for simple random shuffling in code that's both C++03 and C++14?

Background: I'm shuffling the elements of a vector for a simple game. It should be possible to play the same game over again by passing the same integer seed — and vice versa, different seeds should produce different games. Cryptographic security (or any rigor at all) is not a design goal; cleanliness of code is a design goal.


C++98/C++03 introduced std::random_shuffle, which is used like this:



int seed = ...;
std::srand(seed); // caveat, see below

std::vector<int> deck = ...;
std::random_shuffle(deck.begin(), deck.end());


However, as of C++14, random_shuffle has been deprecated (source: N3924). The C++14 way of shuffling a deck is



int seed = ...;

std::vector<int> deck = ...;
std::shuffle(deck.begin(), deck.end(), std::mt19937(seed));


Here are the things that detract from each approach:




  • The srand/random_shuffle way is deprecated in C++14, so we shouldn't use it.




  • On some implementations, random_shuffle appears not to take its seed from srand, i.e., seeding with a different value doesn't produce different output! (libstdc++ on Linux doesn't have this issue, but Xcode on OSX 10.9.5 does.)




  • The shuffle/mt19937 way isn't part of C++03, so we can't use it.




  • The shuffle/mt19937 way appears to require that we pass the seed all the way down into the deck-shuffling code. For my application, I'd prefer to just "set it and forget it" via a mechanism such as srand that hides the global variable, rather than have to define my own global PRNG of type mt19937. In other words: I don't want to be bothered with PRNG details, I just want to shuffle my vector!




  • I am mildly worried about thread-safety (being able to simultaneously shuffle two different decks from different threads), but obviously not about thread-safety and "seedableness" at the same time. Consider thread-safety a "nice-to-have".




The first candidate I've thought of is:




  • bite the bullet and pass int seed all the way down into the deck-shuffling code (avoiding global variables)




  • use something like #if __cplusplus >= 20110000 to use random_shuffle pre-C++11 and shuffle post-C++11




  • to work around the srand "bug" on OSX, use the three-argument version of random_shuffle with some complicated functor... this sounds ugly




The second candidate is:



  • screw C++03; just drop support for any implementation that doesn't provide std::shuffle and std::mt19937 out of the box


But is there some nice way to solve this problem? I know it's a problem that nobody has unless their program is a toy program; but there must be hundreds of toy programs out there that have hit this problem!


Aucun commentaire:

Enregistrer un commentaire