0

I'm following the book Accelerated C++, and to write a function to split a string into a vector of words (separated by space characters), find_if is utilized.

vector<string> split(const string& str) {
    typedef string::const_iterator iter;
    vector<string> ret;

    iter i = str.begin();
    while (i != str.end()) {
        i = find_if(i, str.end(), not_space);

        iter j = find_if(i, str.end(), space);

        if (i != str.end())
            ret.push_back(string(i, j));
        i = j;
    }
    return ret;
}

and the definitions of space and not_space:

bool space(char c) {
    return isspace(c);
}

bool not_space(char c) {
    return !isspace(c);
}

Is it necessary to write two separate predicates here, or could one simply pass !space in place of not_space?

Ethan Roseman
  • 111
  • 1
  • 3
  • 13
  • Your `space` and `not_space` functions are wrong. They must take `unsigned char`. – chris Jul 05 '14 at 19:38
  • @chris How so? It's a templated function accepting "character types", so `char`, `wchar_t`, `unsigned int`, etc. It's perfectly fine to pass `char` only, if it's all you need. – Mario Jul 05 '14 at 19:50
  • That's funny - I was pulling these examples straight from the book. – Ethan Roseman Jul 05 '14 at 19:53
  • @Mario, `space` is certainly not templated. Nor is `isspace`. `isspace` requires the argument to be in the range of `unsigned char`, or `EOF`. Using `char` doesn't cut it. – chris Jul 05 '14 at 19:54
  • Well thanks for the help, @chris. I should tell the author. – Ethan Roseman Jul 05 '14 at 19:55
  • @EthanRoseman, Accelerated C++ is pretty old, and is still pretty good, but being that old, I don't think it will accomplish anything. – chris Jul 05 '14 at 19:56
  • @chris is that really so? Okay. I figured I would learn C++11 after reading this book and I'd have mostly everything covered. What would you recommend I switch to? – Ethan Roseman Jul 05 '14 at 20:02
  • 1
    @EthanRoseman, We have a list of good [books](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). As you can see, Accelerated C++ is on there. It's a good book, but as with all books, time hurts. The really good thing about it is that it teaches you to use these standard algorithms and such, and while it can't teach ones that weren't there when it was written, the point still gets across. – chris Jul 05 '14 at 20:05
  • @chris thanks for the link! I'll definitely look into working with a newer book. – Ethan Roseman Jul 05 '14 at 20:06
  • @chris I based my assumption on documentation such as [this one](http://en.cppreference.com/w/cpp/locale/isspace). I'm actually used to `is...()` being for simple `char` only, while using `isw...()` for wide characters. – Mario Jul 05 '14 at 20:31
  • @Mario, Ah, yes, that one is, but it also takes an extra argument. The one the OP is using is [this](http://en.cppreference.com/w/cpp/string/byte/isspace). – chris Jul 05 '14 at 20:39

3 Answers3

2

Just use std::not1(std::ptr_fun(space)). std::not1 is declared in <functional>.

(There is also a std::not2 for use with binary predicates; std::not1 is for unary predicates.)

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • 3
    `std::not1` will *not* work. The predicate needs to inherit from `std::unary_function` or provide the typedefs provided by `std::unary_function`. You can get around this by using `std::ptr_fun`. – Rapptz Jul 05 '14 at 20:12
1

You cannot simply use !space instead of not_space because all you'll be doing in that case is passing false to find_if. That happens because space will decay to a pointer to function, and function pointers are implicitly convertible to bool. Applying ! to the boolean value will always result in false (because the function pointer is never going to be nullptr).

You can reuse the function space by wrapping it in std::not1, which will negate the result of the predicate passed to it. Unfortunately, it's not as simple as writing std::not1(space), because not1 requires that the predicate define a nested type named argument_type, which your predicate doesn't satisfy.

To convert your function into a predicate usable with not1, you must first wrap it in std::ptr_fun. So the line in your split function becomes:

i = find_if(i, str.end(), std::not1(std::ptr_fun(space)));

With C++11, there's no need for the not1 and ptr_fun shenanigans, just use a lambda expression:

i = find_if(i, str.end(), [](char c) {return !space(c);});
Praetorian
  • 106,671
  • 19
  • 240
  • 328
0

You can also declare

template <bool find_space> bool space(char c) {
    return find_space ^ (!isspace(c));
}

and then refer to it as space<true> and space<false> in the argument to find_if(). Much more versatile than std::not1().

  • 1
    To be honest, I'd prefer `return find_space ? isspace(c) : !isspace(c);` (with appropriately using `std::isspace` and making `c` an `unsigned char`). – chris Jul 05 '14 at 20:18