Grande différence entre mon "nouveau" boulot (depuis Mars 2014) et mon ancien poste universitaire: ici, il y a des revues de code. Et mes collègues "Hergé et Jigé" ont un sacrément haut niveau en C++ comparé au mien. Alors autant profiter de mes deux semaines de "Super Papa Bros" pour essayer de remanier le code de mon moteur de jeu, le rendre plus fiable, plus lisible, et peut-être plus efficace.
J'avais introduit un mécanisme de gestion de mémoire inspiré du cours "compilateurs" : le "tank", avec un seul bloc de mémoire qui est découpé progressivement en sous-blocs qui auront tous la même durée de vie. L'ennui principal, c'est que ce "tank" n'a aucun moyen de retenir quels objets ont été créés ni d'appeler les destructeurs en fin de cycle. Du coup, tout objet "standard" présent dans les morceaux du tank sont une fuite de mémoire potentielle.
Parmi les "nouveaux trucs" appris cette année qui pourront m'être utiles, il y a la fonction "foreach", les fonctions template (et en particulier leur utilisation pour faire de la programmation assertive), les namespaces anonymes, et les structures-internes-pour-masquer-l'implémentation.
Let me collate a few C++ tricks I practiced this year and hope to use in my hobby tools/game engine to improve them.
If it make sense to have a function applied on all members of a collection, foreach
can help:
- for (vector<Tire>::iterator it = wheels.begin(), e = wheels.end(); it != e; it++) {
- checkPressure(*it);
- }
+ for_each(wheels.begin(), wheels.end(), checkPressure);
template<typename T>
void assert(T a, T b, const std::string msg) {
if (a != b) throw AssertException(msg);
}
+ assert(myCar, TimeTravellingDelorean, "timed' out");
-assert<Car>(myCar, TimeTravelling ...);
;
getLibraryPath<sizeof(int)>()
:
template<int> path getLibraryPath();
template<> inline path getLibraryPath<4>() {
return "/usr/lib32";
}
template<> path getLibraryPath<8>() {
return "/usr/lib/x86_64-linux-gnu";
}
template<>
must be out of the class block and have additional MyClass::
token.
You don't need to declare your functions
static
to avoid interference with other translation units of the program. Simply put them in an anonymous namespace.You don't need to explicitly track the "object setup sequence" with an init_level if you can do it with contents of the regular members of the objects,
Car::~Car() {
- switch(init_level) {
- case TIRES_MOUNTED: RecycleTires();
- case ENGINE_INSTALLED: RecycleEngine();
- // FIXME: what do you do for default: ?
- }
+ if (tires!=UNDEF) RecycleTires();
+ if (engine!=UNDEF) RecycleEngine();
You can have compact structure initialization with (optionally-)named fields but it must be *trivial*, e.g.
- you may not swap the order of components;
- you may not omit a field if there are other field after it
- but you *can* omit items at the tail of the description
- copy constructor :
Car(const Car &that) : engine(that.engine), tires(that.tires) {}
- comparison operator :
bool operator==(const Car &that) { return that.tires==tires && that.engine==engine; }
- ostream-compatibility: this requires a additional
std::ostream& operator<<(std::ostream& os, const Car& that) { os << "powered by " << engine << " on " << tires; return os; }
function. Note that it is *not* a member of the Car struct/class and that it will need to be declared friend of the Class in case of a class.
throw new std::runtime_error(..)
is caught by catch (...) { releaseResources(); throw; }
, but not by catch(const exception& e)
. That latest one only catch stack-allocated exceptions, e.g. throw std::runtime_error(...);
. Reading more on this I should.
namespace ds = PPPTeam::LibGEDS
is the way to say import PPPTeam.LibGEDS as ds
. And that ostream & operator << (ostream &out, const Complex &c)
is the way to tell how the class 'Complex' should be printed.
Oh, and I shouldn't use std::unique_ptr
on stack-allocated object. ever. unique_ptr will eventually call free
on the pointer it holds.