[cppx] How to do typed optional arguments in C++98

How can you have strictly typed optional arguments in C++98? Well, for a free-standing routine or just a single class you can use the Named Parameters Idiom, essentially just stuffing all those options into an object that has chainable setter methods, as discussed in the C++ FAQ. But when you get tired of implementing the Named Parameters Idiom ad-hoc in each and every case, or where you need to e.g. extend the set of options in a derived class, you may consider a reusable solution such as the one I’m presenting here.

History & requirements

If you’re not interested in background history then just skip to the next section.

With this posting I’m completing the discussion of the [cppx/arguments] directory, except that I haven’t discussed and won’t discuss cppx::Ref, which just removes a Boost dependency. I’m not discussing everything! On the other hand, since cppx is a Work In Progress™ I may introduce more stuff in this directory and discuss that later.

The history of the cppx optional arguments support goes way back, really way back, and very few if any of the ideas are mine. I just combined the ideas, and possibly someone else has also done that. The current quite general implementation can be traced to a posting that I made in the [comp.lang.c++] Usenet group on 19th July 2009, titled “How can I use unqualified names? (Possibly hard or impossible?)” where I listed these requirements:

a pack of options that may contain a great many simple
numerical and bitset options (defined by external API), and that should be

  1. extensible for at least two levels, like class derivation,
  2. easy to define,
  3. easily constructible specifying only those options of interest, defaulting
    on the rest,
  4. clean notation for accessing options, with compile time
    checking, and
  5. if possible as efficient as possible access of options.

Interesting discussion ensued! :-)

After about a week I combined my original ideas with some offered in that discussion thread, especially a comment by James Kanze, and came up with a scheme that fulfilled the requirements. Including that it’s very easy to use. However, it’s quite complex on the inside, where some advanced implementation techniques are employed.

And after about half a year+ I started looking at this code again, added an ingenious Boost-based macro scheme (effectively variadic macros in C++98!) to address another issue raised by James, and wrote it up as an article for Dr. Dobbs Journal. I got a prompt reply that they’d be back in touch shortly, which is what I have always got from DDJ, followed by complete silence, not even answering my mails, which is also what I have always got from DDJ. And so, very very frustrated, I posted the article and code on Google Docs, and wrote about this being-ignored-by-DDJ-for-13-years in the [comp.lang.c++.moderated] Usenet group…

The [comp.lang.c++.moderated] posting was later referred to by other postings as a recommended way to do optional arguments, but it generated only one direct reply, a “Thank you!” note from someone who apparently had the same simplex communication problem with some of his coworkers. Just ignore the journals, he adviced. Unfortunately I was the moderator processing that response, and as moderator I am obligated to reject pure “Thank you!“ articles. You can imagine that this was even more frustrating! Here I’m posting about being ignored, and have to reject any purely sympathetic response, if it should land in my moderator’s in-box!

The day after that I had calmed down somewhat and thought the only decent thing to do would be to write to DDJ admitting my [comp.lang.c++.moderated] posting, and that I’d given up on them. And for the first time ever this resulted in a DDJ response, very prompt! They were still interested in the article. A plausible explanation for the lack of response was provided. And later on, in May this year, the article was published by DDJ – they messed up the indentation in the code examples, and apparently failed to provide a link to the actual code files, which is why above I also linked to Google Docs instead of just to DDJ, but hey, published! :-)

Using the Boost’ed variant

If you’re using Boost then the cppx optional argument support is particularly easy to use. With the code at Google Docs you just include the [progrock/cppx/collections/options_boosted.h] header and use the CPPX_DEFINE_OPTIONCLASS macro to define an options class, very much like the Named Parameters Idiom. The options class can be named anything you want, e.g. Params or Options, and it can extend an earlier options class which it then is publicly derived from, e.g. as shown below.

Here I’m using my current cppx code which has the header in a different directory – I’ve reorganized the physical packaging a little:

Using [options_boosted.h] with one level of extension:

#include <iostream>
#include <progrock/cppx/arguments/options_boosted.h>

struct AbstractButton
{
    // These members are not part of the cppx options scheme: in actual
    // usage you will instead have e.g. some API level widget states.
    int     hTextAlign;
    int     vTextAlign;
    int     buttonPlacement;

    // Defines a local class 'Options' with specified options & defaults.
    CPPX_DEFINE_OPTIONCLASS( Options, CPPX_OPTIONS_NO_BASE,
        ( hTextAlign,       int,        0   )
        ( vTextAlign,       int,        0   )
        ( buttonPlacement,  int,        0   )
        )

    explicit AbstractButton( Options const& params = Options() )
        : hTextAlign( params.hTextAlign() )
        , vTextAlign( params.vTextAlign() )
        , buttonPlacement( params.buttonPlacement() )
    {}
};

struct CheckBox: AbstractButton
{
    bool    isAuto;
    bool    is3State;

    // Defines an extension of the base class' 'Options' class.
    CPPX_DEFINE_OPTIONCLASS( Options, AbstractButton::Options,
        ( isAuto ,          bool,       true    )
        ( is3State,         bool,       false   )
        )

    explicit CheckBox( Options const& params = Options() )
        : AbstractButton( params )
        , isAuto( params.isAuto() )
        , is3State( params.is3State() )
    {}
};

void show( CheckBox const& cb )
{
    std::cout
        << std::boolalpha
        << "hTextAlign = " << cb.hTextAlign
        << ", isAuto = " << cb.isAuto << ".\n";
}

int main()
{
    typedef CheckBox::Options   CBOptions;

    CheckBox        widget1;
    show( widget1 );                // 0, true (the default values)

    CheckBox        widget2( CBOptions().hTextAlign( 1 ) );
    show( widget2 );                // 1, true

    CheckBox        widget3( CBOptions().hTextAlign( 1 ).isAuto( false ) );
    show( widget3 );                // 1, false
}

But why use a macro like CPPX_DEFINE_OPTIONCLASS when it’s so easy to implement the Named Parameters Idiom oneself (at least on a per case ad-hoc basis)?

Well, for one, the last declaration above, of widget3, is pretty difficult to support with manual ad-hoc options classes: if you try it you’ll see! :-)

The essential problem is that for proper chaining of the option value setters, so that the code compiles, their result types must be covariant, and C++ does not support this kind of automatic covariance – that’s what the macro provides.

Using the un-Boost’ed variant

If you’re not using Boost, and for some reason need to or want to avoid a Boost dependency, then you can use the basic cppx [options.h] header instead of [options_boosted.h].

With the basic [options.h] header you have to specify the number of options in each macro invocation, and also the most work-saving code formatting gets a little unorthodox, as shown below:

Using the basic [options.h] with one level of extension:

#include <iostream>
#include <progrock/cppx/arguments/options.h>

struct AbstractButton
{
    // These members are not part of the cppx options scheme: in actual
    // usage you will instead have e.g. some API level widget states.
    int     hTextAlign;
    int     vTextAlign;
    int     buttonPlacement;

    // Defines a local class 'Options' with specified options & defaults.
    CPPX_DEFINE_3OPTIONCLASS( Options, CPPX_OPTIONS_NO_BASE
        , hTextAlign        , int       , 0
        , vTextAlign        , int       , 0
        , buttonPlacement   , int       , 0
        )

    explicit AbstractButton( Options const& params = Options() )
        : hTextAlign( params.hTextAlign() )
        , vTextAlign( params.vTextAlign() )
        , buttonPlacement( params.buttonPlacement() )
    {}
};

struct CheckBox: AbstractButton
{
    bool    isAuto;
    bool    is3State;

    // Defines an extension of the base class' 'Options' class.
    CPPX_DEFINE_2OPTIONCLASS( Options, AbstractButton::Options
        , isAuto            , bool      , true
        , is3State          , bool      , false
        )

    explicit CheckBox( Options const& params = Options() )
        : AbstractButton( params )
        , isAuto( params.isAuto() )
        , is3State( params.is3State() )
    {}
};

void show( CheckBox const& cb )
{
    std::cout
        << std::boolalpha
        << "hTextAlign = " << cb.hTextAlign
        << ", isAuto = " << cb.isAuto << ".\n";
}

int main()
{
    typedef CheckBox::Options   CBOptions;

    CheckBox        widget1;
    show( widget1 );                // 0, true (the default values)

    CheckBox        widget2( CBOptions().hTextAlign( 1 ) );
    show( widget2 );                // 1, true

    CheckBox        widget3( CBOptions().hTextAlign( 1 ).isAuto( false ) );
    show( widget3 );                // 1, false
}

Getting rid of the “manual”, technically quite redundant and slightly error prone number-of-options specification is what the Boost preprocessor magic provides, but as this program exemplifies one can live without that magic.

After all, you have probably never employed that particular Boost preprocessor magic – it’s an internal and apparently undocumented Boost thing, used by the Boost Parameters Library.

Anyway, cheers, & … enjoy! :-)

About these ads

2 comments on “[cppx] How to do typed optional arguments in C++98

  1. Pingback: 2010 in review | Alf on programming (mostly C++)

  2. Pingback: Is this a design pattern | trouble86.com

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s