Friday, May 18, 2012

Many params in C++

I've been writing about business and sales for too long now.. it's unfortunately what I have to worry about every day.. but I'm still a programmer !

One thing that I've been doing for a while is passing parameters in a structure, when the parameters are too many.
This is nothing new. I think I remember the first usage from an Amiga function that did blit stretching. It was a later version of the OS and the function was pretty slow.. At the time, stretching a bitmap in real-time, was somewhat of a dream.. and it didn't help that Amiga had a bitplanes architecture, which made it extra painful to operate on pixels discretely.

But, anyhow, here's a sample case:

class Image
{
public:
    Image(
        u_int w,
        u_int h,
        u_int rowPitch,
        u_int depth,
        u_int chans,
        u_int flags=0,
        const U8 *pSrcData=NULL );
};

What we have here is a constructor with many parameters.
Two params clearly come with a default value, a third one "rowPitch" could be optional. But, because of how C++ works, we'd have to put it at the end grouped with the other parameters with default values, clearly imposing a dependency on which parameters can be truly optional.

Another thing that I don't like about this, is that it's easy to get confused:

Image img( 320, 240, 0, 32, 4, 0 );

..what is what ?
This gets worse when one starts overloading the constructor with versions that take a different series of parameters.

So, I prefer the following notation:

Image::Params par;
par.width   = 320;
par.height  = 240;
par.depth   = 32;
par.chans   = 4;
Image img( par );

Where the class Image is declared as:

class Image
{
public:
    class Params
    {
    public:
        u_int width;
        u_int height;
        u_int rowPitch;
        u_int depth;
        u_int chans;
        u_int flags;
        const U8 *pSrcData;

        Params() :
            width(0),
            height(0),
            rowPitch(0),
            depth(0),
            chans(0),
            flags(0),
            pSrcData(NULL)
        {
        }
    };

    Image( const Params &par );
};

The declaration is longer, but what matters is the usage.
It's obvious which parameters are being set and anything can be left to default.

The drawback is that sometimes some parameters really ought to be set for the constructor or function to work, and in some other cases some parameters could be mutually exclusive.
So, the constructor in this case will have to assert on the usage at run-time, which is not an ideal thing.
One option is to make the Params constructor itself require some parameters. For example, if width and height must always be specified, then Params would look like this:

Params( u_int width_, u_int height_ ) :
        width(width_),
        height(height_),
        rowPitch(0),

But, I'm not a big fan of this, and it doesn't work for all the cases (mutually exclusive params).

In conclusion, I find that passing parameters by "structure" sometimes helps to improve the code in C++ as well. It's not something new and it's common in Python and Objective-C (to some exent), but it's worth remembering that it's an option.

(Incidentally, I'm currently refactoring my image class 8)

See also the coding page.