Unary functor on algorithm using copy constructor

D

Dijkstra

Hi folks!

First, this is the code I'm using to expose the problem:

------------------------------------------------------------------
#include <functional>
#include <string>
#include <iostream>

using namespace std;

struct space_comparer : public unary_function<char, bool> {

mutable argument_type __lastChar;

space_comparer()
{
__lastChar = 'X';
cout << "ctor() called and lastChar is '" << __lastChar << "'" <<
endl;
}

space_comparer(const space_comparer &__src)
{
cout << "copy ctor() called setting lastChar to 'Y'" << endl;
__lastChar = 'Y';
}

result_type operator ()(const argument_type c) const
{
cout << "[" << __lastChar << " | " << c << "]" << endl;
__lastChar = c;
return ::isspace(c);
}

};


main()
{
string cad1;
string cad2;

const space_comparer instance;

cad1 = "cpp";

remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );

return 0;
}
---------------------------------------------------------------

OK, so the meaning of all that is to use a unary function with a
persistent "state", so that the "space_comparer" works by using
previous values. My problem is that the "remove_copy_if" algorithm,
and I think all algorithms, use a copy.

I mean, suppose this call:

remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
space_comparer() );

This works by constructing a new "space_comparer" functor object and
using its instance for all the algorithm lifetime. What I like to do
is use an actual instance as the predicate, so that it can mantain a
state across algorithm calls.

remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );

I supposed the above call would reuse the "instance" of the object
functor, which indeed does. But it does a temporary copy of the object
and it works with it, leaving the original instance untouched. The
output of the above code is:

-----------------------------------------------------------------
ctor() called and lastChar is 'X'
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
-----------------------------------------------------------------

But, the expected output needs to be like this:

-----------------------------------------------------------------
ctor() called and lastChar is 'X'
[X | c]
[c | p]
[p | p]
[p | c] <<--- Note previous state of "lastChar"
[c | p]
[p | p]
[p | c] <<--- Note previous state of "lastChar"
[c | p]
[p | p]
-----------------------------------------------------------------

I don't know if this is at all possible. I already tried with
"mem_func" and such techniques, but to no avail...

Please, I'm not so STL literate, so bear with me. :)

Cheers,
 
J

Jim Langston

Dijkstra said:
Hi folks!

First, this is the code I'm using to expose the problem:

------------------------------------------------------------------
#include <functional>
#include <string>
#include <iostream>

using namespace std;

struct space_comparer : public unary_function<char, bool> {

mutable argument_type __lastChar;

If you need one, and only one __lastChar (a bad choice of names, by the way)
then you should make it static. I don't think that mutable makes it static.

static argument_type lastChar_;

Underscores in the front of variable names are generally reerved for the
compiler,t here are different rules as to which are and which aren't, it is
best to just never use preceeding underscore for you own variables.
space_comparer()
{
__lastChar = 'X';
cout << "ctor() called and lastChar is '" << __lastChar << "'" <<
endl;
}

space_comparer(const space_comparer &__src)
{
cout << "copy ctor() called setting lastChar to 'Y'" << endl;
__lastChar = 'Y';
}

result_type operator ()(const argument_type c) const
{
cout << "[" << __lastChar << " | " << c << "]" << endl;
__lastChar = c;
return ::isspace(c);
}

};


main()
{
string cad1;
string cad2;

const space_comparer instance;

cad1 = "cpp";

remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );

return 0;
}
---------------------------------------------------------------

OK, so the meaning of all that is to use a unary function with a
persistent "state", so that the "space_comparer" works by using
previous values. My problem is that the "remove_copy_if" algorithm,
and I think all algorithms, use a copy.

I mean, suppose this call:

remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
space_comparer() );

This works by constructing a new "space_comparer" functor object and
using its instance for all the algorithm lifetime. What I like to do
is use an actual instance as the predicate, so that it can mantain a
state across algorithm calls.

remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );

I supposed the above call would reuse the "instance" of the object
functor, which indeed does. But it does a temporary copy of the object
and it works with it, leaving the original instance untouched. The
output of the above code is:

-----------------------------------------------------------------
ctor() called and lastChar is 'X'
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
-----------------------------------------------------------------

But, the expected output needs to be like this:

-----------------------------------------------------------------
ctor() called and lastChar is 'X'
[X | c]
[c | p]
[p | p]
[p | c] <<--- Note previous state of "lastChar"
[c | p]
[p | p]
[p | c] <<--- Note previous state of "lastChar"
[c | p]
[p | p]
-----------------------------------------------------------------

I don't know if this is at all possible. I already tried with
"mem_func" and such techniques, but to no avail...

Please, I'm not so STL literate, so bear with me. :)

Cheers,
 
R

red floyd

Jim said:
If you need one, and only one __lastChar (a bad choice of names, by the way)
then you should make it static. I don't think that mutable makes it static.
In this particular case, the code is bad. Per ISO/IEC 14882:2003, all
identifiers containing a double underscore are reserved to the
implementation (when the standard library is used -- and it is).
 
V

Vishesh

Hi folks!
First, this is the code I'm using to expose the problem:
using namespace std;
struct space_comparer : public unary_function<char, bool> {
mutable argument_type  __lastChar;

If you need one, and only one __lastChar (a bad choice of names, by the way)
then you should make it static.  I don't think that mutable makes it static.

static argument_type lastChar_;

Underscores in the front of variable names are generally reerved for the
compiler,t here are different rules as to which are and which aren't, it is
best to just never use preceeding underscore for you own variables.
space_comparer()
{
__lastChar = 'X';
cout << "ctor() called and lastChar is '" << __lastChar << "'" <<
endl;
}
space_comparer(const space_comparer &__src)
{
cout << "copy ctor() called setting lastChar to 'Y'" << endl;
__lastChar = 'Y';
}
result_type operator ()(const argument_type c) const
{
cout << "[" << __lastChar << " | " << c << "]" << endl;
__lastChar = c;
return ::isspace(c);
}

main()
{
string cad1;
string cad2;
const space_comparer instance;
cad1 = "cpp";
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
return 0;
}
---------------------------------------------------------------
OK, so the meaning of all that is to use a unary function with a
persistent "state", so that the "space_comparer" works by using
previous values. My problem is that the "remove_copy_if" algorithm,
and I think all algorithms, use a copy.
I mean, suppose this call:
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
space_comparer() );
This works by constructing a new "space_comparer" functor object and
using its instance for all the algorithm lifetime. What I like to do
is use an actual instance as the predicate, so that it can mantain a
state across algorithm calls.
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
I supposed the above call would reuse the "instance" of the object
functor, which indeed does. But it does a temporary copy of the object
and it works with it, leaving the original instance untouched. The
output of the above code is:
-----------------------------------------------------------------
ctor() called and lastChar is 'X'
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
-----------------------------------------------------------------
But, the expected output needs to be like this:
-----------------------------------------------------------------
ctor() called and lastChar is 'X'
[X | c]
[c | p]
[p | p]
[p | c]   <<--- Note previous state of "lastChar"
[c | p]
[p | p]
[p | c]   <<--- Note previous state of "lastChar"
[c | p]
[p | p]
-----------------------------------------------------------------
I don't know if this is at all possible. I already tried with
"mem_func" and such techniques, but to no avail...
Please, I'm not so STL literate, so bear with me. :)

As far as I know. The keyword "mutuable" implies that you may change
the value of the variable even though the class/struct is a const.

Eg -
const space_comparer s;
s.__lastChar = 'V'; //totally valid

Now getting to the point. As already stated by Jim you can use a
static member to get the expected results. Predicates are usually not
passed by reference. Thats why the copy constructor is called each
time.
 
V

Vishesh

Hi folks!

First, this is the code I'm using to expose the problem:

------------------------------------------------------------------
#include <functional>
#include <string>
#include <iostream>

using namespace std;

struct space_comparer : public unary_function<char, bool> {

        mutable argument_type  __lastChar;

        space_comparer()
        {
                __lastChar = 'X';
                cout << "ctor() called and lastChar is '" << __lastChar << "'" <<
endl;
        }

        space_comparer(const space_comparer &__src)
        {
                cout << "copy ctor() called setting lastChar to 'Y'" << endl;
                __lastChar = 'Y';
        }

        result_type operator ()(const argument_type c) const
        {
                cout << "[" << __lastChar << " | " << c << "]" << endl;
                __lastChar = c;
                return ::isspace(c);
        }

};

main()
{
        string cad1;
        string cad2;

        const space_comparer instance;

        cad1 = "cpp";

        remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
        remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
        remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );

        return 0;}

---------------------------------------------------------------

OK, so the meaning of all that is to use a unary function with a
persistent "state", so that the "space_comparer" works by using
previous values. My problem is that the "remove_copy_if" algorithm,
and I think all algorithms, use a copy.

I mean, suppose this call:

        remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
space_comparer() );

This works by constructing a new "space_comparer" functor object and
using its instance for all the algorithm lifetime. What I like to do
is use an actual instance as the predicate, so that it can mantain a
state across algorithm calls.

        remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );

I supposed the above call would reuse the "instance" of the object
functor, which indeed does. But it does a temporary copy of the object
and it works with it, leaving the original instance untouched. The
output of the above code is:

-----------------------------------------------------------------
ctor() called and lastChar is 'X'
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
-----------------------------------------------------------------

But, the expected output needs to be like this:

-----------------------------------------------------------------
ctor() called and lastChar is 'X'
[X | c]
[c | p]
[p | p]
[p | c]   <<--- Note previous state of "lastChar"
[c | p]
[p | p]
[p | c]   <<--- Note previous state of "lastChar"
[c | p]
[p | p]
-----------------------------------------------------------------

I don't know if this is at all possible. I already tried with
"mem_func" and such techniques, but to no avail...

Please, I'm not so STL literate, so bear with me. :)

Cheers,

Hi folks!
First, this is the code I'm using to expose the problem:
using namespace std;
struct space_comparer : public unary_function<char, bool> {
mutable argument_type __lastChar;

If you need one, and only one __lastChar (a bad choice of names, by the way)
then you should make it static. I don't think that mutable makes it static.

static argument_type lastChar_;

Underscores in the front of variable names are generally reerved for the
compiler,t here are different rules as to which are and which aren't, it is
best to just never use preceeding underscore for you own variables.
space_comparer()
{
__lastChar = 'X';
cout << "ctor() called and lastChar is '" << __lastChar << "'" <<
endl;
}
space_comparer(const space_comparer &__src)
{
cout << "copy ctor() called setting lastChar to 'Y'" << endl;
__lastChar = 'Y';
}
result_type operator ()(const argument_type c) const
{
cout << "[" << __lastChar << " | " << c << "]" << endl;
__lastChar = c;
return ::isspace(c);
}

main()
{
string cad1;
string cad2;
const space_comparer instance;
cad1 = "cpp";
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
return 0;
}
---------------------------------------------------------------
OK, so the meaning of all that is to use a unary function with a
persistent "state", so that the "space_comparer" works by using
previous values. My problem is that the "remove_copy_if" algorithm,
and I think all algorithms, use a copy.
I mean, suppose this call:
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
space_comparer() );
This works by constructing a new "space_comparer" functor object and
using its instance for all the algorithm lifetime. What I like to do
is use an actual instance as the predicate, so that it can mantain a
state across algorithm calls.
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
I supposed the above call would reuse the "instance" of the object
functor, which indeed does. But it does a temporary copy of the object
and it works with it, leaving the original instance untouched. The
output of the above code is:
-----------------------------------------------------------------
ctor() called and lastChar is 'X'
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
-----------------------------------------------------------------
But, the expected output needs to be like this:
-----------------------------------------------------------------
ctor() called and lastChar is 'X'
[X | c]
[c | p]
[p | p]
[p | c] <<--- Note previous state of "lastChar"
[c | p]
[p | p]
[p | c] <<--- Note previous state of "lastChar"
[c | p]
[p | p]
-----------------------------------------------------------------
I don't know if this is at all possible. I already tried with
"mem_func" and such techniques, but to no avail...
Please, I'm not so STL literate, so bear with me. :)

As far as I know. The keyword "mutuable" implies that you may change
the value of the variable even though the class/struct is a const.

Eg -
const space_comparer s;
s.__lastChar = 'V'; //totally valid

Now getting to the point. As already stated by Jim you can use a
static member to get the expected results. Predicates are usually not
passed by reference. Thats why the copy constructor is called each
time.
 
J

James Kanze

First, this is the code I'm using to expose the problem:
using namespace std;
struct space_comparer : public unary_function<char, bool> {

mutable argument_type __lastChar;

space_comparer()
{
__lastChar = 'X';
cout << "ctor() called and lastChar is '" << __lastChar << "'" <<
endl;
}
space_comparer(const space_comparer &__src)
{
cout << "copy ctor() called setting lastChar to 'Y'" << endl;
__lastChar = 'Y';
}
result_type operator ()(const argument_type c) const
{
cout << "[" << __lastChar << " | " << c << "]" << endl;
__lastChar = c;
return ::isspace(c);
}
};
main()
{
string cad1;
string cad2;
const space_comparer instance;
cad1 = "cpp";
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
return 0;}

OK, so the meaning of all that is to use a unary function with
a persistent "state", so that the "space_comparer" works by
using previous values. My problem is that the "remove_copy_if"
algorithm, and I think all algorithms, use a copy.

The predicate object is passed by copy, so all they have is a
copy to begin with; they have no means of accessing the orginal
object.
I mean, suppose this call:
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
space_comparer() );
This works by constructing a new "space_comparer" functor
object and using its instance for all the algorithm lifetime.
What I like to do is use an actual instance as the predicate,
so that it can mantain a state across algorithm calls.

The problem is that in order to do that, the algorithm would
have to declare the predicate parameter as a reference. A const
reference, if the above is to work.
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
I supposed the above call would reuse the "instance" of the
object functor, which indeed does. But it does a temporary
copy of the object and it works with it, leaving the original
instance untouched. The output of the above code is:
-----------------------------------------------------------------
ctor() called and lastChar is 'X'
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
copy ctor() called setting lastChar to 'Y'
[Y | c]
[c | p]
[p | p]
-----------------------------------------------------------------

Which is what is required by the standard.
But, the expected output needs to be like this:
-----------------------------------------------------------------
ctor() called and lastChar is 'X'
[X | c]
[c | p]
[p | p]
[p | c] <<--- Note previous state of "lastChar"
[c | p]
[p | p]
[p | c] <<--- Note previous state of "lastChar"
[c | p]
[p | p]
-----------------------------------------------------------------
I don't know if this is at all possible. I already tried with
"mem_func" and such techniques, but to no avail...
Please, I'm not so STL literate, so bear with me. :)

A long time ago, there was a saying: "The solution to every
problem is an additional level of indirection." Change your
space_comparer to contain a char* as member, provide a
constructor which takes an char* as argument, and use that,
declaring the char before the predicate.
 
D

Dijkstra

Hi Jim!

If you need one, and only one __lastChar (a bad choice of names, by the way)
then you should make it static.  I don't think that mutable makes it static.

static argument_type lastChar_;

I knew many of you would be instantly caught by the double_underscore
issue. :) I know, I know, it's a bad habit and I promise to flagelate
myself later on.

About the static member attribute, I can't do that since the very use
of static members would kill the multithreaded nature of the program.
The code which used the functor will be automatically non-reentrant,
adding serialization here is killer.

"mutable" attributes can be modified inside "const" functions, i.e.
they don't add to the "const-ness" of the class. Absolutely they don't
become static.

Actually, I come to a solution which I think it's the best suited for
my implementation. It involves using a reference, which is used
instead of the actual member. However, I'm curious about the correct
(and formal) solution, so folks, keep replying... :)

Thanks for your answers,
Dijkstra.
 
D

Dijkstra

Hi red!

In this particular case, the code is bad.  Per ISO/IEC 14882:2003, all
identifiers containing a double underscore are reserved to the
implementation (when the standard library is used -- and it is).

Don't worry red, actually I'm doing this rule in the Makefile:

cat @< | sed s/__/m_/g > ISO_enabled_code_@<
$(CXX) $(CXXFLAGS) -o $@ -c ISO_enabled_code_$<

So as to compile in ISO/IEC enabled mode.

Cheers,
Dijkstra.
 
J

Jim Langston

Hi Jim!

If you need one, and only one __lastChar (a bad choice of names, by the
way)
then you should make it static. I don't think that mutable makes it
static.

static argument_type lastChar_;

I knew many of you would be instantly caught by the double_underscore
issue. :) I know, I know, it's a bad habit and I promise to flagelate
myself later on.

About the static member attribute, I can't do that since the very use
of static members would kill the multithreaded nature of the program.
The code which used the functor will be automatically non-reentrant,
adding serialization here is killer.

"mutable" attributes can be modified inside "const" functions, i.e.
they don't add to the "const-ness" of the class. Absolutely they don't
become static.

Actually, I come to a solution which I think it's the best suited for
my implementation. It involves using a reference, which is used
instead of the actual member. However, I'm curious about the correct
(and formal) solution, so folks, keep replying... :)

----------------------

This is what I came up with playing around. If you use some sort of smart
pointer you may get around having to do the CleanUp. I don't know if thsi
is "right" but seems to be doable. Output is:

ctor() called and lastChar is 'X'
copy ctor() called setting lastCharP
copy ctor() called setting lastCharP
[X | c]
[c | p]
[p | p]
copy ctor() called setting lastCharP
copy ctor() called setting lastCharP
[p | c]
[c | p]
[p | p]
copy ctor() called setting lastCharP
copy ctor() called setting lastCharP
[p | c]
[c | p]
[p | p]

#include <functional>
#include <string>
#include <iostream>
#include <algorithm>

struct space_comparer : public std::unary_function<char, bool> {

mutable argument_type* lastCharP;

space_comparer()
{
lastCharP = new argument_type;
*lastCharP = 'X';
std::cout << "ctor() called and lastChar is '" << *lastCharP << "'"
<< std::endl;
}

void CleanUp() const
{
delete lastCharP;
lastCharP = NULL;
}

space_comparer(const space_comparer &__src)
{
std::cout << "copy ctor() called setting lastCharP" << std::endl;
lastCharP = __src.lastCharP;
}

result_type operator ()(const argument_type c) const
{
std::cout << "[" << *lastCharP << " | " << c << "]" << std::endl;
*lastCharP = c;
return ::isspace(c);
}

};

int main()
{
std::string cad1;
std::string cad2;

const space_comparer instance;

cad1 = "cpp";

remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );
remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad2),
instance );

instance.CleanUp();

return 0;
}
 
D

Dijkstra

Hi All!

I would like to thanks Jim Langston, James Kanze, Vishesh and all of
you who replied with interesting and usefull stuff.

The predicates in the functors are indeed passed by value (copies).
This is required by the standard template library to properly do it's
magic. I don't deeply understand the formality of all this, but in
practice I suspect it has something to do with the fact you can't
double reference something (i.e: &&something --> You can't make a
reference to a reference to something).

The solution I came up with it's pretty much like the one presented by
Jim Langston, but with a twist. Jim used new memory allocations, and
his idea was right in the sense of not using a statically allocated
class member. James Kanze was in this same direction, he suggested
using a constructor which received a "char *" as a parameter, thus
using externally allocated data.

Borrowing ideas from the "copy on write" mechanism of some strings
implementations, I came up with a solution which I think can be
extended to any functor instance which can hold permanent state data.

Here is the code:

-------------------------------------------------------------------
#include <functional>
#include <string>
#include <iostream>

using namespace std;

struct space_compactor : public unary_function<char, bool> {

private:
mutable bool m_data;
bool & m_lastIsSpace;

public:
// Default constructor. Initialize m_lastIsSpace to
// use m_data as the reference
space_compactor()
: m_lastIsSpace(m_data)
{
// We begin with "lastIsSpace == true" so as to erase
// the very first spaces. (Effectively doing a ltrim)
m_lastIsSpace = true;
}

// Copy constructor. Used in function arguments passed by
// value. Initialize m_lastIsSpace in this case as a
// reference of m_date of the original object.
space_compactor(const space_compactor &src)
: m_lastIsSpace(src.m_data)
{
}

result_type operator ()(const argument_type c) const
{
bool spacePresent = ::isspace(c);
bool mustRemove = m_lastIsSpace && spacePresent;
m_lastIsSpace = spacePresent;
return (result_type) mustRemove;
}

};

int main()
{
const string cad1(" this is \t a");
const string cad2(" white space compactor \n");
const string cad3(" \n \n \t functor");
string cad_out;

space_compactor instance;

remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad_out),
instance );
remove_copy_if(cad2.begin(), cad2.end(), back_inserter(cad_out),
instance );
remove_copy_if(cad3.begin(), cad3.end(), back_inserter(cad_out),
instance );

cout << "[" << cad_out << "]" << endl;

return 0;
}
-------------------------------------------------------------------

The output of this little program is:

[this is a white space compactor functor]

So it works ok. No memory allocations, no static members, etc.

Cheers,
Dijkstra.
 
J

James Kanze

The solution I came up with it's pretty much like the one
presented by Jim Langston, but with a twist. Jim used new
memory allocations, and his idea was right in the sense of not
using a statically allocated class member. James Kanze was in
this same direction, he suggested using a constructor which
received a "char *" as a parameter, thus using externally
allocated data.

Which could be on the stack. And which could be initialized
however you wanted.

[...]
struct space_compactor : public unary_function<char, bool> {
private:
mutable bool m_data;
bool & m_lastIsSpace;
public:
// Default constructor. Initialize m_lastIsSpace to
// use m_data as the reference
space_compactor()
: m_lastIsSpace(m_data)
{
// We begin with "lastIsSpace == true" so as to erase
// the very first spaces. (Effectively doing a ltrim)
m_lastIsSpace = true;
}
// Copy constructor. Used in function arguments passed by
// value. Initialize m_lastIsSpace in this case as a
// reference of m_date of the original object.
space_compactor(const space_compactor &src)
: m_lastIsSpace(src.m_data)
{
}

I considered this solution as well, but somehow, it seemed a bit
too subtle. Every instance has an m_data, but only the original
one is used. It runs into problems as well if for some reason,
a copy outlives the originating instance (which, of course,
won't happen if it's only used as a predicate). By having the
user provide the actual data element, you've shoved the
responsibility for the lifetime off onto him:). (The most
robust solution, of course, is dynamic allocation and a
boost::shared_ptr in the implementation. There's something
about dynamically allocating a single bool which gets my hackles
up, however, and when you throw in the added complexity of a
smart pointer on top of that, all for just one little bit...)

I also tend to use a pointer instead of a reference in the
class object, since doing so makes the class assignable, and not
just copiable. I don't think that this is a requirement, but
somehow, it just seems more natural to me that the two go
together. Just a personal preference, I guess.
 
D

Dijkstra

Hi there,

Which could be on the stack.  And which could be initialized
however you wanted.

Agreed, and I don't dislike this mechanism. The only problem is that
this complexity is for a single damned f**king bool. :)

I foresee a possible predicate class which could store more complex
state information. This is the perfect scenario for a external state
class. Perhaps "class persistent_state" which could work in
cooperation with "class persistent_predicate".
I considered this solution as well, but somehow, it seemed a bit
too subtle.  Every instance has an m_data, but only the original
one is used.  It runs into problems as well if for some reason,
a copy outlives the originating instance (which, of course,
won't happen if it's only used as a predicate).

Again, you're right. The m_data instance is a problem, a "resource
leak" if you like.

I will elaborate this and the idea of the shared_ptr deeply. I really
like the idea of the "persistent_predicate" above. :) Now it's only a
bool, but one can think of more complicated states (a stack of
strings, for example).

Cheers,
Dijkstra.
 
G

gpderetta

Hi All!

I would like to thanks Jim Langston, James Kanze, Vishesh and all of
you who replied with interesting and usefull stuff.

The predicates in the functors are indeed passed by value (copies).
This is required by the standard template library to properly do it's
magic. I don't deeply understand the formality of all this, but in
practice I suspect it has something to do with the fact you can't
double reference something (i.e: &&something --> You can't make a
reference to a reference to something).

The solution I came up with it's pretty much like the one presented by
Jim Langston, but with a twist. Jim used new memory allocations, and
his idea was right in the sense of not using a statically allocated
class member. James Kanze was in this same direction, he suggested
using a constructor which received a "char *" as a parameter, thus
using externally allocated data.

Borrowing ideas from the "copy on write" mechanism of some strings
implementations, I came up with a solution which I think can be
extended to any functor instance which can hold permanent state data.

Here is the code:
<snip>

What about tr1::bind?

#include <tr1/functional>
#include <string>
#include <iostream>

using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;

int main()
{

struct trimmer { static bool _(bool &lastIsSpace, const char c)
{
bool spacePresent = ::isspace(c);
bool mustRemove = lastIsSpace && spacePresent;
lastIsSpace = spacePresent;
return mustRemove;
} };

const string cad1(" this is \t a");
const string cad2(" white space compactor \n");
const string cad3(" \n \n \t functor");
string cad_out;

bool lastIsSpace = true;

remove_copy_if(cad1.begin(), cad1.end(), back_inserter(cad_out),
bind(&trimmer::_, ref(lastIsSpace), _1) );
remove_copy_if(cad2.begin(), cad2.end(), back_inserter(cad_out),
bind(&trimmer::_, ref(lastIsSpace), _1) );
remove_copy_if(cad3.begin(), cad3.end(), back_inserter(cad_out),
bind(&trimmer::_, ref(lastIsSpace), _1) );

cout << "[" << cad_out << "]" << endl;

return 0;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top