Is there a C++ equivalent to getcwd?
Asked Answered
S

8

48

I see C's getcwd via: man 3 cwd

I suspect C++ has a similar one, that could return me a std::string .

If so, what is it called, and where can I find it's documentation?

Thanks!

Scopas answered 4/2, 2010 at 20:56 Comment(4)
Why not just use std::string cwd = getcwd(); and let the constructor do it's job?Unavailing
Does getcwd() leak memory if you don't free it? If so, then you should free it after creating the string, as opposed to freeing it when you no longer need it, and that's more convenient. If not, then initializing the string won't leak memory.Taphouse
I can confirm with valgrind - string cwd = getcwd(NULL, 0); does leak memory!Miller
getcwd() allocates the buffer dynamically using malloc(3) if buf is NULLVaunting
F
43

Ok, I'm answering even though you already have accepted an answer.

An even better way than to wrap the getcwd call would be to use boost::filesystem, where you get a path object from the current_path() function. The Boost filesystem library allows you to do lots of other useful stuff that you would otherwise need to do a lot of string parsing to do, like checking if files/directories exist, get parent path, make paths complete etcetera. Check it out, it is portable as well - which a lot of the string parsing code one would otherwise use likely won't be.

Update (2016): Filesystem has been published as a technical specification in 2015, based on Boost Filesystem v3. This means that it may be available with your compiler already (for instance Visual Studio 2015). To me it also seems likely that it will become part of a future C++ standard (I would assume C++17, but I am not aware of the current status).

Update (2017): The filesystem library has been merged with ISO C++ in C++17, for

std::filesystem::current_path();
Fichte answered 4/2, 2010 at 22:25 Comment(12)
Of course, in some situations it may not be worth including the boost::filesystem library just to get the current working directory. Though, if s/he is doing lots of filesystem stuff, then they may as well use boost for all of it.Byrdie
Why is every answer boost::this boost::that?Labarum
@chx101: I don't think Boost is always the answer. But in some cases it is, and using something that already exists and makes your life easier is usually a good thing. Note that some things that are now in the C++ standard came from Boost and that Boost Filesystem is slated for inclusion as a technical specification iirc.Fichte
The reason I said that is because whenever someone asks how to achieve a certain task using an API(s), the answer is always: use this library. It makes sense if you're working on a big project and you have time to carve to get acquainted with your new library, but for an application that just wants to get the current working directory you want to download a library just for that? Third party libraries just make it harder to debug your app and like in this case add nothing but bloat and the result is a big fat file.Labarum
@chx101: If I understand correctly, you are saying that it is unnecessary to grab a (big) third party library just to get the current working directory. This part I can agree with to some extent, however the point is that you are not likely just interested in the directory - you are going to use that for something, likely manipulating file paths. Instead of then writing something of your own, which likely is going to be buggy and nonportable you can instead use something that already exists and works. Boost may be too big for some use cases, but you don't need to get it all.Fichte
@chx101: Then you say that "third party libraries just make it harder to debug your app" and "add nothing but bloat". This part I cannot agree with. Why waste your time debugging your own code if there is something else that already does what you want to do? I would instead argue that using (well written) third party libraries makes it easier to debug your own code, since you write less code.Fichte
@Fichte When you incorporate third party code, it'll more likely hinder or slow down your application debugging process. Some libraries need to initialize and therefore must run before your code does. In that process they may do shit that'd make it hard to debug your code, which comes after the library's."Why waste your time debugging your own code if there is something else that already does what you want to do?" If it's too big for a simple task.Labarum
@Fichte >> "using (well written) third party libraries makes it easier to debug your own code, since you write less code" So then what stops you from using your own (well written) code with just exactly enough functions for your task?Labarum
@chx101: You always have a choice whether to use a library or to rewrite the same/better/partial functionality yourself. It's an engineering decision that depends on where you think your time is best spent, risk of problems, ease of use, ease of maintenance, etc. IME, it is usually better to use something that already exists - but it is impossible to give an absolute rule as your use cases are probably different from mine. Also, IME, the self-written library replacements people write tend to be buggy, non-portable, missing functionality, and a nightmare to use. You don't appear to agree, ok.Fichte
Maybe it depends on who's writing the code. Someone who knows what they're coding is more likely to have less to no bugs.Labarum
And my initial reaction was not a disagreement, I code in Assembly and when sometimes I'm looking up how to achieve something on Windows I usually get the answer use this library or that library when I know that reading the Windows API plus a little research I can roll out my on in Assembly. So yeah. Let's keep things the way they are and just give people our vulnerable libraries and watch people get hacked because the software vendor didn't know what their 3rd party code actually does.Labarum
@chx101: Thanks for proving my point... :) Another example why rolling your own is usually a bad thing is this question itself. ATM there are 6 other answers, none of which are portable nor handle unicode paths on Windows. Another point: Boost Filesystem is essentially what will become the Filesystem Technical Specification for C++. IMHO the choice between Boost and hand-rolled assembly for normal things like this is a no brainer for most applications. One of the reasons that there are lots of vulnerable code in the wild is that people write their own instead of using something existing.Fichte
P
30

std::string's constructor can safely take a char* as a parameter. Surprisingly there's a windows version too.

Edit: actually it's a little more complicated:

std::string get_working_path()
{
   char temp[MAXPATHLEN];
   return ( getcwd(temp, sizeof(temp)) ? std::string( temp ) : std::string("") );
}

Memory is no problem -- temp is a stack based buffer, and the std::string constructor does a copy. Probably you could do it in one go, but I don't think the standard would guarantee that.

About memory allocation, via POSIX:

The getcwd() function shall place an absolute pathname of the current working directory in the array pointed to by buf, and return buf. The pathname copied to the array shall contain no components that are symbolic links. The size argument is the size in bytes of the character array pointed to by the buf argument. If buf is a null pointer, the behavior of getcwd() is unspecified.

Playbill answered 4/2, 2010 at 20:59 Comment(10)
is the char * automatigally freed? or does this give me a memory leak?Scopas
Both this answer and Liranuna's comment use getcwd as a no-argument function, yet the documentation I see shows two parameters. Am I reading the wrong docs?Trifocals
@Scopas no, the resulting char* is malloc ed and should be free d after creating a std::string from it.Ioab
@Bill, that's a non-standard extension, as far as I can tell. Linux, Mac, and Windows all implement it, which may or may not be "portable enough."Trifocals
@Bill, the POSIX specification explicitly states that data is copied, not allocated, hence no freeing is needed.Playbill
Sorry, I was looking at the no-parameter version before the edit changed it to take a buffer. If you use the no-parameter version (or the non-standard version that mallocs if given a NULL buffer) then you will need to use free.Ioab
-1 Potential buffer overflow. Bad code. ( will allocate std::string based on garbage in temp if getcwd fails, garbage may not be nul terminated; you need to check return value from ::getcwd and throw on error )Nicolenicolea
@Pete, exceptions are not a solution that can be used always - refrain from wanting to throw everywhere. However the error checking was ommited for verbosity as usual in such cases. Where do you see a buffer overflow?Playbill
If an error occurs, then the contents of temp are undefined. As you were creating a string using the single argument constructor expecting a nul terminated string, the constructor could scan off the end before you hit a nul. Hence, buffer (read) overflow. Your edited code does not have this issue, but may cause weird behaviour in whatever is using the return value if it does fail, unless the client tests for an empty string. Personally, I wouldn't wrap the posix function at all, and let the client code test the documented POSIX result rather than testing for empty.Nicolenicolea
@RobKennedy undefined here on OSX 10.6Nepean
N
13

Let's try and rewrite this simple C call as C++:

std::string get_working_path()
{
    char temp [ PATH_MAX ];

    if ( getcwd(temp, PATH_MAX) != 0) 
        return std::string ( temp );

    int error = errno;

    switch ( error ) {
        // EINVAL can't happen - size argument > 0

        // PATH_MAX includes the terminating nul, 
        // so ERANGE should not be returned

        case EACCES:
            throw std::runtime_error("Access denied");

        case ENOMEM:
            // I'm not sure whether this can happen or not 
            throw std::runtime_error("Insufficient storage");

        default: {
            std::ostringstream str;
            str << "Unrecognised error" << error;
            throw std::runtime_error(str.str());
        }
    }
}

The thing is, when wrapping a library function in another function you have to assume that all the functionality should be exposed, because a library does not know what will be calling it. So you have to handle the error cases rather than just swallowing them or hoping they won't happen.

It's usually better to let the client code just call the library function, and deal with the error at that point - the client code probably doesn't care why the error occurred, and so only has to handle the pass/fail case, rather than all the error codes.

Nicolenicolea answered 4/2, 2010 at 22:54 Comment(2)
There's a typo. It should be != 0 instead of == 0 on the fifth line.Gallinacean
C++ is the language that has all the features you don't need, and you implement all the ones you needMuttonhead
F
10

You'll need to just write a little wrapper.

std::string getcwd_string( void ) {
   char buff[PATH_MAX];
   getcwd( buff, PATH_MAX );
   std::string cwd( buff );
   return cwd;
}
Freemason answered 4/2, 2010 at 21:6 Comment(2)
maybe try std::string getcwd_string( void ) { string ret(PATH_MAX,0); getcwd( &ret[0], PATH_MAX ); ret.resize(ret.find_first_of('\0',0)); return ret; } - avoiding VLA's (which is technically not part of c++ standards, at least not yet, albeit most compilers support it as an extension to the language)Landward
You have to check for nullptr (NULL) returned from getcwd.Valencia
T
4

All C functions are also C++ functions. If you need a std::string, just create one from the char* that getcwd gets for you.

Trifocals answered 4/2, 2010 at 20:59 Comment(0)
K
4

I used getcwd() in C in the following way:

char * cwd;
cwd = (char*) malloc( FILENAME_MAX * sizeof(char) );
getcwd(cwd,FILENAME_MAX);

The header file needed is stdio.h. When I use C compiler, it works perfect.

If I compile exactly the same code using C++ compiler, it reports the following error message:

identifier "getcwd" is undefined

Then I included unistd.h and compiled with C++ compiler. This time, everything works. When I switched back to the C compiler, it still works!

As long as you include both stdio.h and unistd.h, the above code works for C AND C++ compilers.

Kennykeno answered 30/10, 2013 at 14:18 Comment(2)
+1 for verifying in both compilers, and re-verifying it after changes.Restorative
Use PATH_MAX instead of FILENAME_MAXBellicose
W
1

I also used boost::filesystem as stated in another answer above. I just wanted to add that since the current_path() function does not return a std::string, you need to convert it.

Here is what I did:

std::string cwd = boost::filesystem::current_path().generic_string();
Wedged answered 15/6, 2017 at 16:24 Comment(0)
I
-3

You could create a new function, which I would prefer over linking to a library like boost(unless you already are).

 std::string getcwd()
 {
     char* buff;//automatically cleaned when it exits scope
     return std::string(getcwd(buff,255));
 }
Ignition answered 7/7, 2017 at 18:21 Comment(2)
buff points to uninitialized memory, and would segfault if you're luckyHultin
Oops! yes, allocate the memory.Ignition

© 2022 - 2024 — McMap. All rights reserved.