Boost logo

Boost :

Subject: Re: [boost] [program_options]
From: Domagoj Saric (domagoj.saric_at_[hidden])
Date: 2010-03-08 06:31:36


"vicente.botet" <vicente.botet_at_[hidden]> wrote in message
news:06810FA68C344871BDFED63A5595AF0D_at_viboes1...
...
> What about having both overloads? const char * is the best for literals, while
> string cont& is the best for strings variables.
>
> void option_description::set_name(string const&_name);
> void option_description::set_name(const char* _name);
...

Well, looking at the implementation of set_name() posted above:

option_description::set_name(const char* _name)
{
    std::string name(_name);
    string::size_type n = name.find(',');
    if (n != string::npos) {
        assert(n == name.size()-2);
        m_long_name = name.substr(0, n);
        m_short_name = '-' + name.substr(n+1,1);
    } else {
        m_long_name = name;
    }
    return *this;
}

it can be noticed that it, in the case where a comma is present in the
string, makes 4 to 6 allocations (and copies) when it should make 0 to 2...and
in the 'other' case makes 1 or 2 allocations when it should make 0 or 1...

If we change the implementation as follows:

typedef iterator_range<char const *> string_chunk_t;

option_description::set_name( string_chunk_t const name )
{
    string_chunk_t::const_iterator const pComma( std::find( name.begin(),
        name.end(), ',' ) );
    if ( pComma != name.end() ) {
        assert( pComma == name.end() - 2 );
        char const short_name[ 2 ] = { '-', name.back() };
        m_short_name.assign( boost::begin( short_name ), boost::end(
           short_name ) );
    }
    m_long_name.assign( name.begin(), pComma );
    return *this;
}

we solve both the inefficency issues outlined above as well as the char const *
/ std::string const & overload issues. It will even eliminate the strlen() calls
for literals.

Unfortunately, because of certain issues with boost::range (that I will go into
more detail in a separate post) the following two overloads are currently
also necessary:

template <class Range>
option_description::set_name( Range const & string_range, typename
disable_if<is_pointer<Range>>::type * = 0 )
{
    char const * const begin( &* boost::begin( string_range ) );
    char const * const end ( ( &*( boost::end ( string_range ) - 1 ) ) + 1 );
    assert( is_array<Range>::value == ( *(end - 1) == '\0' ) );
    set_name( string_chunk_t( begin, is_array<Range>::value ? end - 1 : end ) );
}

template <class Range>
option_description::set_name( Range const & name, typename
enable_if<is_pointer<Range>>::type * = 0 )
{
    set_name( boost::as_literal( name ) );
}

ps. "From the looks of that assertion" it seems the suggested implementation can
be further improved by eliminating even the std::find() operation:

option_description::set_name( string_chunk_t const name )
{
    string_chunk_t::const_iterator pComma( name.end() );
    if ( *( name.end() - 2 ) == ',' ) {
        pComma = name.end() - 2;
        char const short_name[ 2 ] = { '-', name.back() };
        m_short_name.assign( boost::begin( short_name ), boost::end(
            short_name ) );
    }
    else
        assert( std::find( name.begin(), name.end(), ',' ) == name.end() );

    m_long_name.assign( name.begin(), pComma );
    return *this;
}

Now, if this is necessary, the strong exception safety guarantee could be
provided by first calling reserve() on m_long_name at the beginning of the
function....

--
"What Huxley teaches is that in the age of advanced technology, spiritual
devastation is more likely to come from an enemy with a smiling face than from
one whose countenance exudes suspicion and hate."
Neil Postman

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk