How return a std::string from C's “getcwd” function

≡放荡痞女 提交于 2019-12-20 01:58:35

问题


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 find 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)


回答1:


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.




回答2:


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.




回答3:


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.




回答4:


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, ...
}



回答5:


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.




回答6:


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;
}



回答7:


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());
}


来源:https://stackoverflow.com/questions/2869594/how-return-a-stdstring-from-cs-getcwd-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!