Search code examples
c++templatesmaintainabilitycode-readability

Increase readability & maintainability : omit < > for many variable declaration possible?


The question seems very abstract. I will ask by an example instead.

Introduction

Assume that I have many types of game objects.

They are Bullet, Rocket, Enemy, Zone, ... .

They are all nicely created & deleted & managed by pools, e.g.

Pool<Bullet> poolBullet ; 
Pool<Rocket> poolRocket ;  

The game logic would manage object in form of Pool_Handle< xxx >, e.g.

Pool_Handle< Bullet >  bullet = poolBullet.create() ;
Pool_Handle< Rocket>  rocket = poolRocket .create() ;

Problem

Now, I look at the refactoring.

For example, if my old code was ...

Bullet* functionA(Rocket* rocket){
    for(int n=0;n<rocket->zones.size();n++){
        Zone* zone = rocket->zones.get(n);
        Bullet* return_value =zone.functionB();
    }
}

... it would become ...

Pool_Handle<Bullet> functionA(Pool_Handle<Rocket> rocket){
    for(int n=0;n<rocket->zones.size();n++){
        Pool_Handle<Zone> zone = rocket->zones.get(n);
        Pool_Handle<Bullet> return_value =zone.functionB();
    }
}

Notice that they are now Pool_Handle everywhere.

After reading and editing these thousands line codes for a few days, I have grown accustomed to Pool_Handle even more than any game objects. It might be a word that I type fastest now.

Question

How to retain readability and maintainability to equal level of the old code, and if possible, how to reduce time of typing for the variable template?

I don't expect a perfect answer, because every solution I found has some trade-off.

My poor solutions

/**  remedy 1 **/
template <class TT> using H = Pool_Handle<TT>; //remedy line
H<Bullet> bullet ; //alleviate symptom, better but not perfect

/**  remedy 2 **/
using H_Bullet  = Pool_Handle<H_Bullet>; //remedy line
H_Bullet bullet ; //looks good, but have limitations

/** remedy 3 **/
auto bullet;  //concise, but can't be used everywhere, sometimes also reduce readability

The second solution seems to be nice, but introduces several limitations.

  • I have to declare a line for Bullet, Rocket, ... and so on, one by one

  • If there is a new type of game object, I will have to add another line manually.

  • If a game object is renamed, e.g. Bullet -> MetalBullet, I will also have to change the remedy line to H_MetalBullet manually.

  • Therefore, it lowers overall maintainability.

Are there any better approaches?

(Edit) You can assume c++11, c++14, whatever.
(Edit) A platform independent solution is favorable.

Edit 1 (To clarify even more why the second solution is not perfect)

In the second solution, I have to add "another" line after declare a new class.

The line is ...

using H_Bullet  = Pool_Handle<H_Bullet>; 

Where should I place it?

  1. inside Bullet.h ;

    It will create bad coupling, because Bullet should not know about the Handle at all.

  2. inside some high-level header that is included by every game logic files ;

    The result is that there are 2 different places that has some definitions about Bullet.
    More places -> Less maintainability.

Both places will yield a certain minor disadvantage : When I call some automatic refactoring I have to call twice, on both Bullet and H_Bullet.


Solution

  • What's wrong with a header that just forward declares the types you're interested in? It will only change when you add new types to your system that need handles, and if you rename / add something it will break gracefully.

    types_fwd.hpp

    template <typename> Pool;
    template <typename> Pool_Handle;
    
    class Bullet;
    class Rocket;
    
    using H_Bullet = Pool_Handle<Bullet>;
    using H_Rocket = Pool_Handle<Rocket>;
    

    The only other option would be to invert things and forward declare the pool and handle and include / add that to each header that introduces new types that need handles.