-
STL style
In my current pet project I try to use the STL as much as possible. There's no real reason except to learn how it works, doing it the old way would probably save me a lot of headaches and even more time. This is a simple function and I could have written it in about 10 other ways to make it work. The goal is to make it work STL style. However, my STL style obsession has it's limits, I'm not going to write a ten line functor of my own to supply to a three line STL function when I can have the whole thing done in one simple loop myself.
The function digits_only takes a string and removes everything that is not a digit according to the given locale. It took me a while to recognize the remove_if is not really "removing if". However, it takes a single argument to decide if a container element is to be removed, a so-called predicate, a function that takes a container element and returns a bool.
I tried various variants of what I thought should have worked but was greeted with a lot of cryptic error messages. Sorry for copying only generic english error messages, but I use a German version and those messages are probably even less helpful.
Code:
#include <string>
#include <iostream>
#include <cctype>
#include <algorithm>
#include <locale>
#include <functional>
using namespace std;
void digits_only( std::wstring& s, const locale& loc = locale("") )
{
wstring::iterator erasor = remove_if( s.begin(), s.end(), std::isdigit );
// works fine... except for not doing what I want, I need the digits to stay and remove only the non-digits
// plus, as of now it's not recognizing the supplied locale
// trying to add a logical not for removing anything that's not a digit:
//wstring::iterator erasor = remove_if( s.begin(), s.end(), logical_not( std::isdigit ) );
// error C2440: 'conversion' : cannot convert from 'type1' to 'type2'
//wstring::iterator erasor = remove_if( s.begin(), s.end(), logical_not( std::isdigit<wchar_t> ) );
// error C2440: 'conversion' : cannot convert from 'type1' to 'type2'
//wstring::iterator erasor = remove_if( s.begin(), s.end(), not1( std::isdigit<wchar_t> ) );
// error C2784: 'declaration' : could not deduce template argument for 'type' from 'type'
// gave up and tried to bind the locale as a second parameter first
//wstring::iterator erasor = remove_if( s.begin(), s.end(), bind2nd( std::isdigit, loc ) );
// error C2896 'function1 ' : cannot use function template 'function2' as argument.
//wstring::iterator erasor = remove_if( s.begin(), s.end(), bind2nd( std::isdigit<wchar_t>, loc ) );
// error C2784 'declaration' : could not deduce template argument for 'type' from 'type'
s.erase( erasor, s.end() );
}
int main()
{
wstring text = L"a1b2c3";
digits_only( text );
wcout << text << endl;
return 0;
}
-
Code:
s.erase(
std::remove_if( s.begin(), s.end(),
std::bind2nd(std::isdigit, loc)
),
s.end()
);
The bind2nd could be better replaced with boost::bind or std::tr1::bind:
Code:
namespace p = std::tr1::placeholders;
// ...
std::tr1::bind(std::isdigit, p::_1, loc)
The syntax is, I think, more intuitive.
-
hm, I never heard of tr1, interesting read, it has a lot of things I could really use, although I think our perception of "intuitive" differs :)
Does the first line you posted compile for you ? I get three errors, C2896 once and C2784 twice on that line and I wonder why, it looks correct. I'm using VS2003.
-
It does not, in fact. I forgot that bind2nd can only bind to functors with the correct nested typedefs, which a function reference/pointer isn't. Also, std::isdigit is a template, so you have to explicitly instantiate it. This leads to this line:
Code:
std::bind2nd(std::ptr_fun(std::isdigit<char>), loc)
And this still doesn't work, because apparently it forms a reference to a reference.
All in all, std::tr1::bind is so far superior that you really should use it (or boost::bind, if you don't have std::tr1 yet) instead.
As for what is intuitive, it's syntax you'll quickly get familiar with. You might also want to learn functional languages to appreciate function composition. (And then laugh at C++ for its clumsy syntax. ;))
-
Well, I can't use boost at work and VS doesn't have tr1, so I'm going to loop myself :( Thanks anyway.
-
You could also create your own function for remove_if() to use:
Code:
wstring::iterator erasor = remove_if( s.begin(), s.end(), is_not_digit );
.
.
bool is_not_digit(wchar_t c)
{
return !std::isdigit(c);
}
I tried playing around with not1(isdigit), but also couldn't get it to compile. I also std::tr1, but my compiler apparently doesn't have it.