How return a std::string from C's "getcwd" function
Sorry to keep hammering on this, but I'm trying to learn :). Is this any good? And yes, I care about memory leaks. I can't f开发者_开发知识库ind a decent way of preallocating the char*, because there simply seems to be no cross-platform way.
const string getcwd()
{
char* a_cwd = getcwd(NULL,0);
string s_cwd(a_cwd);
free(a_cwd);
return s_cwd;
}
UPDATE2: without Boost or Qt, the most common stuff can get long-winded (see accepted answer)
If you want to remain standard, getcwd
isn't required to do anything if you pass to it a NULL; you should instead allocate on the stack a buffer that is "large enough" for most occasions (say, 255 characters), but be prepared for the occasion in which getcwd
may fail with errno==ERANGE
; in that case you should allocate dinamically a bigger buffer, and increase its size if necessary.
Something like this could work (notice: not tested, just written by scratch, can be surely improved):
string getcwd()
{
const size_t chunkSize=255;
const int maxChunks=10240; // 2550 KiBs of current path are more than enough
char stackBuffer[chunkSize]; // Stack buffer for the "normal" case
if(getcwd(stackBuffer,sizeof(stackBuffer))!=NULL)
return stackBuffer;
if(errno!=ERANGE)
{
// It's not ERANGE, so we don't know how to handle it
throw std::runtime_error("Cannot determine the current path.");
// Of course you may choose a different error reporting method
}
// Ok, the stack buffer isn't long enough; fallback to heap allocation
for(int chunks=2; chunks<maxChunks ; chunks++)
{
// With boost use scoped_ptr; in C++0x, use unique_ptr
// If you want to be less C++ but more efficient you may want to use realloc
std::auto_ptr<char> cwd(new char[chunkSize*chunks]);
if(getcwd(cwd.get(),chunkSize*chunks)!=NULL)
return cwd.get();
if(errno!=ERANGE)
{
// It's not ERANGE, so we don't know how to handle it
throw std::runtime_error("Cannot determine the current path.");
// Of course you may choose a different error reporting method
}
}
throw std::runtime_error("Cannot determine the current path; the path is apparently unreasonably long");
}
By the way, in your code there's a very wrong thing: you are trying to dellocate a_cwd (which presumably, in the nonstandard extension, is allocated with malloc or with some other memory allocation function, since getcwd is thought for C) with delete
: you absolutely shouldn't do that, keep in mind that each allocation method has its deallocation counterpart, and they must not be mismatched.
You must not pass a null pointer to the constructor of a std::string
, so you must check the buffer pointer getcwd()
returns isn't null. Also, the buffer pointer you pass to getcwd()
must not be null.
std::string getcwd() {
char buf[FILENAME_MAX];
char* succ = getcwd(buf, FILENAME_MAX);
if( succ ) return std::string(succ);
return ""; // raise a flag, throw an exception, ...
}
This will work on Windows and Linux, since they both support the automatic allocation behavior when the buf
argument to getcwd
is NULL. However, be aware that this behavior is not standard, so you may have issues on more esoteric platforms.
You can do it without relying on this behavior, though:
const string getcwd()
{
size_t buf_size = 1024;
char* buf = NULL;
char* r_buf;
do {
buf = static_cast<char*>(realloc(buf, buf_size));
r_buf = getcwd(buf, buf_size);
if (!r_buf) {
if (errno == ERANGE) {
buf_size *= 2;
} else {
free(buf);
throw std::runtime_error();
// Or some other error handling code
}
}
} while (!r_buf);
string str(buf);
free(buf);
return str;
}
The above code starts with a buffer size of 1024, and then, if getcwd
complains that the buffer is too small, it doubles the size and tries again, and repeats until it has a large enough buffer and succeeds.
Note that calling realloc
with its first argument as NULL is identical to malloc
.
You're supposed to use the ISO C++ conformant version _getcwd
I think. There's no point returning a const string
, and you should use free
to deallocate (at least according to MSDN):
string getcwd()
{
char* a_cwd = _getcwd(NULL, 0);
string s_cwd(a_cwd);
free(a_cwd);
return s_cwd;
}
Of course you should also check if _getcwd()
returns NULL.
You need to check for a_cwd being NULL. Then it will work on Mac, Windows, Linux. However, it's not POSIX-compliant.
EDIT: perror doesn't exit the program, so you should exit, throw an exception, or do something.
How about this? It's short, exception safe, and doesn't leak.
std::string getcwd() {
std::string result(1024,'\0');
while( getcwd(&result[0], result.size()) == 0) {
if( errno != ERANGE ) {
throw std::runtime_error(strerror(errno));
}
result.resize(result.size()*2);
}
result.resize(result.find('\0'));
return result;
}
When "string constructor" do everything for you:
#include <stdio.h> // defines FILENAME_MAX
#include <unistd.h> // for getcwd()
std::string GetCurrentWorkingDir()
{
std::string cwd("\0",FILENAME_MAX+1);
return getcwd(&cwd[0],cwd.capacity());
}
精彩评论