const callback

K

Kyku

Hello all,

I'm in a process of writing a small Linux C++ program for discovering
repeaded files over a filesystem. One part of this task is traversing a
directory structure. This is done by the following recursive function.
Since I'd like it to be general enough to handle function pointers and
function objects I've made it a template.

template <typename CallBack>
void Traverse(const std::string& path, CallBack& callback) throw
(PosixError);

This brings an interesting problem: if I make second parameter is const
(and I'd like it to be that way) then:

1) G++ 3.3.6 refuses to compile the program
error: no matching function for call to `Traverse(char*&, void
(&)(const std::string&))'

2) G++ 4.1.1 compiles the program but the callback is never executed
(i.e. no output) if the callback is function. Works fine if given a
function objects.

Is it some kind of compliler issue or maybe a C++ mistake in my code?
TIA, Kyku

Here's the working part of the program:

#include <string>
#include <cstddef>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <errno.h>

class PosixError : public std::exception
{
public:
PosixError() : _M_code(errno), _M_rep(strerror(_M_code)) {}
PosixError(int code) : _M_code(code), _M_rep(strerror(_M_code)) {}
int Code() const { return _M_code; }
const char* what() const throw() { return _M_rep.c_str(); }
~PosixError() throw() {}

private:
int _M_code;
std::string _M_rep;
};

class DirEntry
{
public:
const std::string& Name() { return _M_name; }
operator bool () const { return !_M_name.empty(); }

private:
DirEntry(struct dirent* dent) : _M_name(dent ? dent->d_name : "") {}

std::string _M_name;
friend class DirWalker;
};

class DirWalker
{
public:
DirWalker(const std::string& dirname) throw (PosixError) {
_M_dir = 0;
Open(dirname);
}

~DirWalker() { if (_M_dir != 0) closedir(_M_dir); }

operator bool () const { return _M_dir != 0; }

void Open(const std::string& dirname) throw (PosixError) {
Close();
_M_dir = opendir(dirname.c_str());
if (_M_dir == 0) throw PosixError();
}

void Close() {
if (_M_dir != 0) {
closedir(_M_dir);
_M_dir = 0;
}
}

DirEntry Read() throw(PosixError) {
const char* name;
struct dirent* dent;
do {
errno = 0;
dent = readdir(_M_dir);
if (errno != 0) throw PosixError();
name = dent->d_name;
} while (dent != 0 && name[0] == '.' && (name[1] == '\0' || name[1]
== '.' && name[2] == '\0'));
return DirEntry(dent);
}

private:
DIR* _M_dir;
};


template <typename CallBack>
void Traverse(const std::string& path, CallBack& callback) throw
(PosixError) {
DirWalker dw(path);
while (DirEntry de = dw.Read()) {
try {
struct stat buf;
std::string fullpath = path + '/' + de.Name();
if (stat(fullpath.c_str(), &buf) < 0) throw PosixError();
callback(fullpath);
if (S_ISDIR(buf.st_mode)) Traverse(fullpath, callback);
} catch (PosixError& pe) {
if (pe.Code() == EACCES) continue;
throw;
}
}
}

#include <iostream>

void PrintString(const std::string& name) { std::cout << name <<
std::endl; }

int main(int argc, char *argv[]) {
for (int i = 1; i < argc; ++i)
Traverse(argv, PrintString);
}
 
J

Jens Theisen

Kyku said:
Is it some kind of compliler issue or maybe a C++ mistake in my code?
TIA, Kyku

I occurs to me that both behaviours are (different) compiler bugs,
though version 3.4.4 actually gets it right. :)

A look at the disassembly shows that 4.1 indeed doesn't emit any code
for the callback call if it's given by const reference.

I'm not an expert, but I don't think that's correct.

Jens
 
T

tolgaceylanus

When I try this;

Traverse(argv[1],(void (*)(const std::string &)) PrintString);

Then it works... Interesting...

Tolga Ceylan
 
K

Kyku

(e-mail address removed) napisal(a):
When I try this;

Traverse(argv[1],(void (*)(const std::string &)) PrintString);

Then it works... Interesting...

Tolga Ceylan

I've recently found out that it also works if the function is preceded
by an ampersand:

Traverse(argv, &PrintString);

This compiles and works correctly in both v3 and v4. But then I always
thought that a function name on its own is equivalent to its address,
so these two invocations should be equivalent.

I've added the following line at the top of Traverse():

std::cout << __PRETTY_FUNCTION__ << std::endl;

The version with ampersand always (both g++3 and g++4) says:

void Traverse(const std::string&, const CallBack&) [with CallBack =
void (*)(const std::string&)]

the version without ampersand states (in g++4 ):

void Traverse(const std::string&, const CallBack&) [with CallBack =
void ()(const std::string&)]

What is "void ()(const std::string&)" is mistery to me (neither pointer
nor reference). Waiting for your input guys and gals.
 

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top