Problem using mem_fun.

R

Robbie Hatley

I've got a function that I use a lot when making utility programs
that need to do the same thing to every directory in a tree.
Its prototype is:

unsigned long int CursDirs (void Func(void));

This just applies the fuction Func to every subdirectory of the
current directory. It works fine when I pass it pointers to regular
void-void functions.

But last night I had a situation where it would be very handy to pass
it a pointer to a member function of a class. This is in a program
that counts lines of text in all *.c, *.cpp, and *.h files in a
directory tree, starting at current directory and recursively descending.
(Basically, "line count of this project".) In this program, I have
this class:

class SourceTree
{
public:
SourceTree(void) : Lines(0UL) {}
void CountLines(void);
void PrintLines(void);
private:
void CountLinesInFile (const std::string& FileName);
void CountLinesInCurDir (void);
unsigned long int Lines;
};

I tried to pass &SourceTree::CountLinesInCurDir to CursDirs:

SourceTree::CountLines(void)
{
CursDirs(&SourceTree::CountLinesInCurDir);
return;
}

but OOPS, that's not allowed!

So I tried to use mem_fun from <functional> like this:

SourceTree::CountLines(void)
{
CursDirs(mem_fun(&SourceTree::CountLinesInCurDir));
return;
}

But that doesn't work either. I get an error message saying
"cannot convert std::mem_fun_t<void, SourceTree> to void(*)()"

What am I doing wrong here? Is there a way to get mem_fun to
work in this application, or is this too far removed from it's
original intended purpose (ie, std. algorithms)?

(In the mean time, I had to copy&paste the entire body of CursDirs
into CountLines. This works, but is very messy. It'd sure be nice
to be able to do it in one line instead.)

--
Cheers,
Robbie Hatley
Tustin, CA, USA
email: lonewolfintj at pacbell dot net
web: home dot pacbell dot net slant earnur slant
 
J

John Harrison

Robbie Hatley said:
I've got a function that I use a lot when making utility programs
that need to do the same thing to every directory in a tree.
Its prototype is:

unsigned long int CursDirs (void Func(void));

This just applies the fuction Func to every subdirectory of the
current directory. It works fine when I pass it pointers to regular
void-void functions.

But last night I had a situation where it would be very handy to pass
it a pointer to a member function of a class. This is in a program
that counts lines of text in all *.c, *.cpp, and *.h files in a
directory tree, starting at current directory and recursively descending.
(Basically, "line count of this project".) In this program, I have
this class:

class SourceTree
{
public:
SourceTree(void) : Lines(0UL) {}
void CountLines(void);
void PrintLines(void);
private:
void CountLinesInFile (const std::string& FileName);
void CountLinesInCurDir (void);
unsigned long int Lines;
};

I tried to pass &SourceTree::CountLinesInCurDir to CursDirs:

SourceTree::CountLines(void)
{
CursDirs(&SourceTree::CountLinesInCurDir);
return;
}

but OOPS, that's not allowed!

So I tried to use mem_fun from <functional> like this:

SourceTree::CountLines(void)
{
CursDirs(mem_fun(&SourceTree::CountLinesInCurDir));
return;
}

But that doesn't work either. I get an error message saying
"cannot convert std::mem_fun_t<void, SourceTree> to void(*)()"

What am I doing wrong here? Is there a way to get mem_fun to
work in this application, or is this too far removed from it's
original intended purpose (ie, std. algorithms)?

The return from mem_fun is a functor, not a function pointer. In any case
mem_fun doesn't help you in this particular case because you have no way to
pass the class itself into CurDirs.

You need a refresher course in functors.

The first thing you should do is rewrite CursDirs as a template so that it
can use either a function pointer or a functor.

template <class F>
unsigned long int CursDirs (F func)
{
...
func();
...
}

Then you should write your own functor that can call the member function you
want

struct LineCounter
{
LineCounter(SourceTree* tree) : _tree(tree) {}
void operator()() const
{
tree->CountLinesInCurDir();
}
private:
SourceTree* _tree;
};

Finally you use it like this

SourceTree::CountLines(void)
{
CursDirs(LineCounter(this));
}

This is untested code, but hopefully you get the idea.

john
 
R

Robbie Hatley

Greetings said:
The return from mem_fun is a functor, not a function pointer. In any case
mem_fun doesn't help you in this particular case because you have no way to
pass the class itself into CurDirs.


Aye, I can see now, that is just the problem: CursDirs does not
have any way of calling CountLinesInCurDir through the "this"
pointer of the one and only SourceTree instance in my program.

You need a refresher course in functors.

The first thing you should do is rewrite CursDirs as a template so that it
can use either a function pointer or a functor.

template <class F>
unsigned long int CursDirs (F func)
{
...
func();
...
}


I did that, and it works fine. I can see how the template
parameter F makes func more "generic" so that CursDirs
will now work with either a function or a functor.

Thanks for the tip!

Then you should write your own functor that can call the member function you
want

struct LineCounter
{
LineCounter(SourceTree* tree) : _tree(tree) {}
void operator()() const
{
tree->CountLinesInCurDir();
}
private:
SourceTree* _tree;
};

Finally you use it like this

SourceTree::CountLines(void)
{
CursDirs(LineCounter(this));
}

This is untested code, but hopefully you get the idea.

john


Yes, that works very well! I actually put two such functors
in my program based around your "LineCounter", one to pass
a SourceTree member function to three "for_each" statements,
and the other to pass a SourceTree member function to CursDirs.

Perhaps I should try to templatize those as generic "Glue Functors".
Could be very useful for allowing functions and algorithms
to call member functions of types other than the argument type.

Thanks again for the help!


--
Cheers,
Robbie Hatley
Tustin, CA, USA
email: lonewolfintj at pacbell dot net
web: home dot pacbell dot net slant earnur slant
 

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

Staff online

Members online

Forum statistics

Threads
473,731
Messages
2,569,432
Members
44,832
Latest member
GlennSmall

Latest Threads

Top