Opened 13 years ago

Closed 13 years ago

Last modified 13 years ago

#4020 closed Support Requests (fixed)

regex::match_results::format functor cannot use member variables

Reported by: Keith MacDonald <keith@…> Owned by: John Maddock
Milestone: Boost 1.43.0 Component: regex
Version: Boost 1.42.0 Severity: Problem
Keywords: regex functor Cc:

Description

regex::match_results::format makes a copy of its functor argument. This is restrictive, because it prevents the functor from managing its state using member variables. Is that intentional?

The attached VC++ 2008 code demonstrates the problem. When it uses a member variable to maintain a count, its output is:

(1)The (1)quick (1)brown (1)fox (1)jumped (1)over (1)the (1)lazy (1)dog

Making the counter global instead gives the correct output:

(1)The (2)quick (3)brown (4)fox (5)jumped (6)over (7)the (8)lazy (9)dog

Attachments (1)

RegexFunctor.zip (5.6 KB ) - added by Keith MacDonald <keith@…> 13 years ago.

Download all attachments as: .zip

Change History (5)

by Keith MacDonald <keith@…>, 13 years ago

Attachment: RegexFunctor.zip added

comment:1 by Steven Watanabe, 13 years ago

This is normal behavior for interfaces that accept function objects. Put the variables outside the function object and store a pointer or reference to them.

comment:2 by anonymous, 13 years ago

As Steven says, this is the expected behaviour.

However, I will try and make that clearer in the docs, and I'll trivially update the code so that it works with boost::ref.

John.

comment:3 by John Maddock, 13 years ago

Resolution: fixed
Status: newclosed

(In [60678]) Add support for Boost.Ref in match_results::format. Update docs accordingly. Fixes #4020.

comment:4 by Keith MacDonald <keith@…>, 13 years ago

Thanks for the clarification. Returning to C++ after a break, I had forgotten that it was a standard library convention to pass functors by value. Anyway, I've amended my example code to take account of that, and you're welcome to use it in the documentation:

// Code to insert an incremented count before each word in the source text.

#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <boost/regex.hpp>
#include <boost/shared_ptr.hpp>

typedef wchar_t         char_t;
typedef std::wstring    string_t;

template <class M>
class functor
{
public:
    functor() : m_pImpl(new functor_data) {}

    template <class O>
    O operator()(const M& m, O i)
    {
        // Generate the sequence number in brackets:
        char_t  buf[16];
        O       i2(i);

        *i2++ = L'(';
        _itow(++m_pImpl->nCounter, buf, 10);
        i2 = std::copy(buf, buf + wcslen(buf), i2);
        *i2++ = L')';

        // Copy the matched text;
        return m.format(i2, L"$0");
    }

private:
    struct functor_data
    {
        functor_data() : nCounter(0) {}
        int nCounter;
    };

    boost::shared_ptr<functor_data> m_pImpl;
};

int wmain(int argc, char_t* argv[])
{
    string_t    sText(L"The quick brown fox jumped over the lazy dog");
    string_t    sRE(L"\\w+");

    boost::regex_constants::match_flag_type matchFlags =
        boost::regex_constants::match_default;

    boost::basic_regex<char_t> e;
    e.assign(sRE, matchFlags);

    typedef string_t::const_iterator        string_iterator;
    boost::regex_iterator<string_iterator>  reit(sText.begin(), sText.end(), e, matchFlags);
    boost::regex_iterator<string_iterator>  reEnd;
    boost::match_results<string_iterator>   last_m = *reit;
    std::basic_ostringstream<char_t>        stream;
    std::ostream_iterator<char_t, char_t>   streamIter(stream);

    functor<boost::match_results<string_iterator> > func;

    for ( ; reit != reEnd; ++reit) {
        const boost::match_results<string_iterator>&    m = *reit;

        // copy the source up to this match:
        std::copy(m.prefix().first, m.prefix().second, streamIter);

        // replace the matched text:
        m.format(streamIter, func);
        last_m = m;
    }

    // copy the remainder of the source string:
    std::copy(last_m.suffix().first, last_m.suffix().second, streamIter);

    // output the result:
    std::wcout << stream.str() << std::endl;

    return 0;
}
Note: See TracTickets for help on using tickets.