c++ - have program get absolute path its directory

Shadow2531

[H]ard|Gawd
Joined
Jun 13, 2003
Messages
1,670
Using Mingw 3.4.2 on WinXP Sp2

Say if I have:
"c:\directory\program.exe"
"c:\directory\file.txt"

and in program.exe, I have ifstream in("file.txt").

Then I:

start
run
cmd
cd \
"c:\directory\program.exe"


program.exe won't be able to find "file.txt", because it's looking for file.txt at "c:\file.txt" instead of "c:\directory\file.txt".

Basically, when I call a program from a different directory than the program is in, the current working directory is the directory I called the program from instead of the directory the app is in.

1.) Does that work the same way on other OSs like mac and linux?

2.) What's the best way for a program to figure what directory it is in no matter where it's called from that also works on linux if necessary?


For windows, I can do something like this:

Code:
#include <iostream>
#include <string>
#include <fstream>
#include <windows.h>

using namespace std;

int main() {
    TCHAR path[2048] = {0};
    GetModuleFileName( NULL, path, 2048 );
    const string exe_path( path );
    const string file_path( exe_path.substr(0, exe_path.rfind("\\") + 1 ) + "file.txt" );
    ifstream in( file_path.c_str() );
    if (!in) {
        return 1;
    }
    for (string s; getline(in,s); ) {
        cout << s << endl;
    }
}

However, WinXP supports longer paths than win9X/ME and maybe Win2k also. I want to be able to take advantage of longer paths in WinXP and other OSs that support longer paths. Do I have to use GetModuleFileNameExW to do that? What size should I use for the array then? 32768?

Also, should I use another method to get just the path (instead of copying to a string and then substringing it) ?

Do I have to worry about anything when using TCHAR instead of char?

That should cover most of my questions about windows, but what about linux? ( I'd be testing on debian woody with whatever gcc version that comes with it )

I've been told before that's there's no cross-OS way of doing it, so I'd have to set up #ifdef conditionals to do what I want.

Of course you all may have better ideas. :)

Thanks
 
Try boost::filesystem. It may be overkill for what you want, but it has portable path objects and operations plus a bunch of other useful stuff.

http://www.boost.org
 
You can try looking at argv[0], but I think the standard doesn't specify that this must have a path to it. So I don't think there's a cross-platform solution. You could get into parsing argv[0] and "apply" it as a relative path to the current working directory; I think that could be made to work.

I couldn't imagine a shell that would set the current directory before starting the program when then the program was given a relative path. It would also have to adjust the arguments to the program -- and it doesn't know if "foo.txt" really means the text "foo.txt", or is referring to a file in the current directory. So how would it know to adjust the string or not?

Do I have to worry about anything when using TCHAR instead of char?
If you're going to compile for unicode, then yes. Otherwise, no -- and in that case, I have to wonder why you're using TCHAR to begin with.

Remember to test how your program work when I run it from a network share:

start
run
cmd
\\yourcomputer\share\directory\program.exe

Is file.txt static? That is, can the user edit it to set up your configuration? If not, then why not bake it into a resource? That way, it's built-in to your executable.

As a nit,
Code:
 TCHAR path[2048] = {0};
This kind of initialization makes me cringe. You're painting a two kilobyte buffer with zeros, then setting a string into the buffer in the next statement. You're copying the string again, then modifying it and copying it again.
 
bassman said:
Try boost::filesystem. It may be overkill for what you want, but it has portable path objects and operations plus a bunch of other useful stuff.

http://www.boost.org

I have boost setup for mingw and have built the filesystem lib. However, can you show a simple example of using boost filesystem so I can see if something like that would work out. I know there are examples on the site, but they seem a little confusing.
 
mikeblas said:
You can try looking at argv[0], but I think the standard doesn't specify that this must have a path to it. So I don't think there's a cross-platform solution. You could get into parsing argv[0] and "apply" it as a relative path to the current working directory; I think that could be made to work.

I can kind of make that work, but I was told not to because there's no guarantee it'll be right.

I couldn't imagine a shell that would set the current directory before starting the program when then the program was given a relative path. It would also have to adjust the arguments to the program -- and it doesn't know if "foo.txt" really means the text "foo.txt", or is referring to a file in the current directory. So how would it know to adjust the string or not?

I'm not sure, but that's what windows does for me.

If you're going to compile for unicode, then yes. Otherwise, no -- and in that case, I have to wonder why you're using TCHAR to begin with.

I don't really know if I need to compile for unicode chars in the path. I'd like to support it if possible. Though. Anyway, asking around, I was told that I had to use TCHAR because that's what the function expects. ( I have tried char before and it worked just fine though )

Remember to test how your program works when I run it from a network share:
noted

Is file.txt static? That is, can the user edit it to set up your configuration? If not, then why not bake it into a resource? That way, it's built-in to your executable.

file.txt is expected to be in whatever directory program.exe is, which could be in any directory or even on a usb drive or disk. So, no. The location of file.txt cannot be hardcoded.

As a nit,
Code:
 TCHAR path[2048] = {0};
This kind of initialization makes me cringe. You're painting a two kilobyte buffer with zeros, then setting a string into the buffer in the next statement. You're copying the string again, then modifying it and copying it again.

Should I do it like this then?

Code:
string getPath() {
    TCHAR path[2048];
    GetModuleFileName( NULL, path, 2048 );
    PathRemoveFileSpec( path );
    string p( path );
    return p;
}

const string file( getPath() + "\\file.txt" );

(PathRemoveFileSpec() - shlwapi.h and link to libshlwapi.a )

Also, I think GetModuleFileName() returns an int for success/failure. I should probably check that before moving on.

Also, since you are so keen on optimization, would

string file( getPath() );
file += "\\file.txt";

be any better?

Thanks


Or, I could do it like this:

Code:
#include <iostream>
#include <sstream>
#include <fstream>
#include <windows.h>
#include <shlwapi.h>

using namespace std;

int main() {
    TCHAR path[2048];
    if ( !GetModuleFileName( NULL, path, 2048 ) || !PathRemoveFileSpec( path ) ) {
        return 1;
    }
    ostringstream file;
    file << path << "\\file.txt";
    ifstream in( file.str().c_str() );
    if (!in) {
        return 1;
    }
    for (string s; getline(in,s); ) {
        cout << s << endl;
    }
}
 
Shadow2531 said:
I'm not sure, but that's what windows does for me.
Windows certainly does not adjust paths to files given on the command line. If you do

foo.exe bar.txt

you get "bar.txt" as a parameter, no matter what. Windows doesn't try to give you ".\bar.txt" or "c:\something\your\path\bar.txt".

Shadow2531 said:
I don't really know if I need to compile for unicode chars in the path. I'd like to support it if possible. Though. Anyway, asking around, I was told that I had to use TCHAR because that's what the function expects. ( I have tried char before and it worked just fine though )
TCHAR is a macro defined as "char" if you're not building unicode, and "wchar_t" if you're building unicode. MSDN describes it in great detail, as do other references; my book discusses it in the chapter on strings, IIRC.

TCHAR.H documentation in MSDN.

Shadow2531 said:
file.txt is expected to be in whatever directory program.exe is, which could be in any directory or even on a usb drive or disk. So, no. The location of file.txt cannot be hardcoded.
I'm not asking about the location of file.txt; I'm asking about its content. If the content of the file is static, then why have a seperate file at all? Just bake it into your resources it becomes part of your executable.

Shadow2531 said:
Should I do it like this then?
That's a step in the right direction. You're using less memory, but still aren't optimal. You're copying the string three times when you don't need to copy it even once.

Shadow2531 said:
Also, I think GetModuleFileName() returns an int for success/failure. I should probably check that before moving on.
I would think it's something that you'd just assert. GetModuleFileName() isn't going to fail unless there's something catastropically wrong.

Shadow2531 said:
Also, since you are so keen on optimization, would
What I'm saying is that creating a std::string is the wrong thing to do here. You're getting a string of characters from the API and sticking them in an array. You always make the string shorter, not longer, and the manipulation you do is very simple. So just do the manipulation in the array directly. Each time you create a std::string, you copy the string and allocate memory. Any time you have "const string", you should be suspect; why are you dynamically allocating memory and using a value management class for something that's never going to change? If it's going to change, why did you make it const? If you only reference it once, why did you even allocate it?

For your application, it hardly matters; you're doing this once. But if you're in the habit of programming like this, you're hurting yourself.

Sorry to be such a pendant about it -- it's what I do for a living. Certainly, you're better off trying to get your requirements sorted out before you approach optimizing your solution.
 
Code:
string file( getPath() );
file += "\\file.txt";

As proof that I can't leave well enough alone, consider the above code. You're calling GetPath(), so I'll assume you're using the implmentation you provided earlier in your note. Then, you're constructing a new string out of it, and then appending a constant string.

So:

Allocate memory. Copy a string you already have into it.
Measure the string. Allocate as much memory as you had. Copy one to the other.
Destroy the first one.
Measure the string again. Measure the constant string. Allocate memory for the sum.
Copy the string. Copy the constant string. Free the second one.

That should strike you as a lot of work -- it should be intuitive that's way more work than you need to be asking the computer to do. Note that there's another measure/allocate/copy in your getpath() function which I don't mention above.

Maybe you're lucky, and you have a very good implementation of the STL which does ref counting. That might eliminate one copy/reallocate.

Is this the only place you'll use getpath()? If so, then why wouldn't you bake all this into getpath()?

Again, if you're doing this once at startup, it probably isn't a big deal. If you do it as a practice, then your code is inefficient and you should think a little harder about the side-effects of what it is you're writing.

This code is far worse -- a strinstream?
Code:
    ostringstream file;
    file << path << "\\file.txt";
    ifstream in( file.str().c_str() );

Here's a good experiment for you to do: step into the implementation of these routines. What does "<<" do in the above example when you trace into it? Can you find the extra allocations and initializations it is doing? What about the ostringstream constructor?

What does the middle line there do, anyway -- just concatenate "\file.txt" onto the end of path, right? So why wouldn't you just do that directly with a function like strcat()? If you're going to do lots more complicated juggling with this string, fine -- use std::string and make your life a little easier.

For now, though, you're spending a lot and getting essentially no value in return.
 
mikeblas said:
Windows certainly does not adjust paths to files given on the
you get "bar.txt" as a parameter, no matter what.

I'm saying that when you use a relative path to a file in ifstream, the resolved path to the file is the current working directory + the relative path to the file. The problem is, the current working directory is not always the same directory that the program is in, so the resolved path to the file can be incorrect.

I should be able to use ifstream in("file.txt") and count on it loading the file.txt that's in the same directory as the app, but that doesn't work always.

TCHAR is a macro defined as "char" if you're not building unicode, and "wchar_t" if you're building unicode. MSDN describes it in great detail, as do other references; my book discusses it in the chapter on strings, IIRC.

O.K. I think I'll worry about unicode at a later time.

I'm not asking about the location of file.txt;

file.txt content would not be static.

I would think it's something that you'd just assert. GetModuleFileName() isn't going to fail unless there's something catastropically wrong.

O.K.

What I'm saying is that creating a std::string is the wrong thing to do here. You're getting a string of characters from the API and sticking them in an array. You always make the string shorter, not longer, and the manipulation you do is very simple. So just do the manipulation in the array directly. Each time you create a std::string, you copy the string and allocate memory. Any time you have "const string", you should be suspect; why are you dynamically allocating memory and using a value management class for something that's never going to change? If it's going to change, why did you make it const? If you only reference it once, why did you even allocate it?

I use const whenever something is not going to be change. As for dealing with the array directly, I try to avoid that like the plague any time I have to add something together, which is why I copy to a string and deal with it there. It's safer in my case. :)

Sorry to be such a pendant about it -- it's what I do for a living. Certainly, you're better off trying to get your requirements sorted out before you approach optimizing your solution.

That's true, but I certainly don't mind. Thanks.
 
mikeblas said:
Code:
string file( getPath() );
file += "\\file.txt";

As proof that I can't leave well enough alone, consider the above code. ......That should strike you as a lot of work.

Yes, when you put it that way.

Is this the only place you'll use getpath()? If so, then why wouldn't you bake all this into getpath()?

It may be reused for other things.

This code is far worse -- a strinstream?
Code:
    ostringstream file;
    file << path << "\\file.txt";
    ifstream in( file.str().c_str() );

Here's a good experiment for you to do: step into the implementation of these routines. What does "<<" do in the above example when you trace into it? Can you find the extra allocations and initializations it is doing? What about the ostringstream constructor?

I was told that stringstream is far more efficient for building strings (which is what I'm doing) than doing string + this or string += that.

What does the middle line there do, anyway -- just concatenate "\file.txt" onto the end of path, right? So why wouldn't you just do that directly with a function like strcat()? If you're going to do lots more complicated juggling with this string, fine -- use std::string and make your life a little easier.

For now, though, you're spending a lot and getting essentially no value in return.

O.K., how bout this?

Code:
#include <iostream>
#include <sstream>
#include <fstream>
#include <windows.h>
#include <shlwapi.h>

using namespace std;

int main() {
    char path[2048];
    GetModuleFileName( NULL, path, 2048 );
    PathRemoveFileSpec( path );
    strcat(path, "\\file.txt");
    ifstream in( path );
    if (!in) {
        return 1;
    }
    for (string s; getline(in,s); ) {
        cout << s << endl;
    }
}

However, the thing is, I need to make sure that the path set with PathRemoveFileSpec + "\\file.txt" does not exeed the limit 2047 for example, so what I really need to do is check the size of path to make sure there's room for \file.txt and if not, stop right there.

Something like, if strlen(path) > 2038, return 1. (Or is it, 2037)
 
Shadow2531 said:
I have boost setup for mingw and have built the filesystem lib. However, can you show a simple example of using boost filesystem so I can see if something like that would work out. I know there are examples on the site, but they seem a little confusing.

Assuming argv[0] contains the full path to your executable (which may be part of your portability problem), you can do this:

Code:
using boost::filesystem;

path myExe(argv[0]);

// get the directory the binary is in
path exeDir = myExe.branch_path();

// now create a full path to the file we want
path file = exeDir / "file.txt";

// get the platform specific pathname string
std::string filename = file.native_file_string();

Note: I didn't try compiling or running this, but you get the idea.
 
On windows argv[0] will contain exactly what you type in to execute the program, so if your in a root directory and the program is under some other directory and you execute it with the path to its directory argv[0] will contain that same path. So in your example you typed "c:\directory\program.exe", argv[0] will contain the same exact thing you typed "c:\directory\program.exe".

If your concerned with having paths resolved to the directory your program resides you can consider everything before your programs filename to be the path to your program. Im not sure if this is true of all operating systems, but its true for windows. I''l go ahead and check cygwin and linux cause I have them handy, I will get back with those results later.

Edit: Cygwin behaves the same and oddly enough when used outside its environment it still uses /home if its in its home directory still or /cygdrive/path if outside it. For some reason my linux live cd's arent working so im doing a linux virtual pc install in the background.

Im sure that linux will behave the same though and im sure the idea behind having it not find the file is intentional to allow the program to work in the current directory locally.

Edit2: Ah I'll let some one else confirm that its the same on linux, after going through all that trouble to install a linux distro on virtual pc, my router didnt like my virtual machine's http requests. Somethings wrong when you can install the operating system faster than you can download gcc. Now that I think of it I can ssh into my universities unix machine and test it there. close enough =)

Edit: Yeah it was the same on solaris, so its safe to say thats the way its done. So if you really want to hardcode a path into your program you can check argv[0] for slashes and assume everything before the last slash is what must be before the files you are accessing that should be in your programs directory.

Last edit: My connection is seriously screwed, wasnt just virtual pc. I changed my connection plan and im guessing something wasnt switched correctly at the dsl station. So sorry if I cant make all these edits pretty.
 
Lord of Shadows said:
On windows argv[0] will contain exactly what you type in to execute the program, so if your in a root directory and the program is under some other directory and you execute it with the path to its directory argv[0] will contain that same path. So in your example you typed "c:\directory\program.exe", argv[0] will contain the same exact thing you typed "c:\directory\program.exe".

I think you're correct, but I can't agree with you.

argv[0] isn't set by Windows; it's set by the runtime library. That's why you're finding CYGWIN behaves differently -- it has a different runtime library. I think it's also conceivable that the shell might do something to change what you type -- provide the OS something other than what you've typed in order to implement a feature that lets a program run from a non-path or non-current directory.

Here's what the standard says, BTW:

Section 3.6.1 said:
If argc is nonzero these arguments shall be supplied in argv[0] through
argv[argc-1] as pointers to the initial characters of null-terminated multibyte strings (NTMBSs)
(17.3.2.1.3.2) and argv[0] shall be the pointer to the initial character of a NTMBS that represents the
name used to invoke the program or "".
 
@Lord of Shadows

So you're saying I could do *something like* this for example and it'd work across OSs?

Code:
#include <iostream>
#include <string>

using namespace std;

string makeAbsPathIfNeeded(char p[], const string& file) {
    string s( p );
    if ( s.find("\\") != string::npos ) {
        return s.substr( 0, s.rfind( "\\" ) ) + "\\" + file;
    } else if ( s.find("/") != string::npos) {
        return s.substr( 0, s.rfind( "/" ) ) + "/" + file;
    } else {
        return file;
    }
}

int main(int argc, char* argv[] ) {
    cout << makeAbsPathIfNeeded( argv[0], "file.txt" ) << endl;
}
 
Your code works on cygwin, on windows normally and through network shares, and on my universities unix system. I'd bet money that it would work on most linux/freebsd distros, you may want to get a mac guy to test it out, but assuming they use slashes like everyone else in the world it should.

Now im not saying this isnt a hacky way of doing it, but I dont think there exists non hacky way of doing it. =)

-=style nudging=-
tabs > spaces
brackets inline > not inline

I liked your way of brackets before the other way was forced upon me and now I perfer how much easier things are when they do line up. Although I still like doing "} else {" in a single line. Breaking that apart is just a waste of vertical space to me.
 
-=style nudging=-
tabs > spaces
brackets inline > not inline

I liked your way of brackets before the other way was forced upon me and now I perfer how much easier things are when they do line up. Although I still like doing "} else {" in a single line breaking that apart is just a waste of vertical space to me.

STL drilled that style into my head and now I just have to do it or I'll get withdrawals.

I can't stand to use tabs. It just bugs me. (Also don't like the 8-space-length of tabs when posting on forums)
 
Shadow2531 said:
STL drilled that style into my head and now I just have to do it or I'll get withdrawals.

Hah! How do you know Stephan?
 
mikeblas said:
Hah! How do you know Stephan?
He might know him from when he used to post on this forum. (That's how I found nuwen.net in the first place.) The guy would always get into arguments over the inferiority of Java. Those argument threads were truly funny but unfortunately he was banned and I think his posts were deleted.
 
That's amusing. I'll have to give him some flack at work.
 
mikeblas said:
Hah! How do you know Stephan?

From these forums, and he has helped me privately basically in the same way as you by showing me that I can do things much simpler. He used to tell me to quit using all those damned substr() all the time.

In fact, your two coding styles, confidence, recommendations, optimizations, and efficiency are almost equivalent. I often get the feeling that you are Stephan posting as Mike Blas. :)

It's a shame that STL got kicked off here. He was nothing but help IMO. Too many people mistook his confidence as arrogance and got all defensive. He, like you, knows what he's talking about.

In fact, he posts in the same manner as you, but I don't see you getting kicked off here. Too many just got sick of how good he is and complained and got him banned. I've read many of those argument threads and, to be honest, I don't see how STL pissed everyone off.

STL is not afraid to say if he doesn't know something either.

Also, every thread that STL posted in got wiped out not too long after he was banned. That means that all the threads he helped me in were destroyed.
 
Shadow2531 said:
@Lord of Shadows

So you're saying I could do *something like* this for example and it'd work across OSs?

Code:
#include <iostream>
#include <string>

using namespace std;

string makeAbsPathIfNeeded(char p[], const string& file) {
    string s( p );
    if ( s.find("\\") != string::npos ) {
        return s.substr( 0, s.rfind( "\\" ) ) + "\\" + file;
    } else if ( s.find("/") != string::npos) {
        return s.substr( 0, s.rfind( "/" ) ) + "/" + file;
    } else {
        return file;
    }
}

int main(int argc, char* argv[] ) {
    cout << makeAbsPathIfNeeded( argv[0], "file.txt" ) << endl;
}


I think that might be a problem in linux for example, if I were to do something like this:

/directory with spaces/program.exe
/directory with spaces/ file.txt

If in /, I type

./directory\ with\ spaces/program.exe

my little function might think that's windows. (I really need to setup linux again. video card configuration is a pain though).

I can switch the conditional around or do #ifdef _WIN32 etc. and do things separately if needed, but I need to do more testing first to see if the argv[0] method will even work.
 
Shadow2531 said:
From these forums, and he has helped me privately basically in the same way as you by showing me that I can do things much simpler. He used to tell me to quit using all those damned substr() all the time.

He's right; it's a pretty expensive function, and you often don't need it.

Similarities aside, I'm not him. I was flaming people on mailing lists when he was on the school lunch program.

It's a little creepy that his posts were all deleted. What was his screen name? STL?

Anyway, thank you for your kind words.
 
Shadow2531 said:
./directory\ with\ spaces/program.exe

On the unix server argv[0] was all cleaned up already, but Im sure if you look hard enough you can find pleanty of ways to screw this up. =)
 
Lord of Shadows said:
On the unix server argv[0] was all cleaned up already, but Im sure if you look hard enough you can find pleanty of ways to screw this up. =)

Thanks.

I'm gonna have to take a trip over to the linux forum and get some things sorted out soon.
 
Lord of Shadows said:
On the unix server argv[0] was all cleaned up already, but Im sure if you look hard enough you can find pleanty of ways to screw this up. =)

I tested on debian woody.

If you have for example:

"/home/user/Desktop/directory with spaces/test"
"/home/user/Desktop/directory with spaces/file.txt"

and use ifstream in("file.txt") in program "test"

and

cd /

and do

./home/user/Desktop/directory\ with\ spaces/test

file.txt won't be found because the current working directory is "/" and not "/home/user/Desktop/directory with spaces"


So I can confirm that the problem occurs on linux too.

I can also confirm that the following solution works on both windows and linux.

Code:
string generatePath(char p[], const string& file) {
    const string s( p );
    if ( s.find("/") != string::npos) {
        return s.substr( 0, s.rfind( "/" ) ) + "/" + file;
    } else if ( s.find("\\") != string::npos ) {
        return s.substr( 0, s.rfind( "\\" ) ) + "\\" + file;
    } else {
        return file;
    }
}

const string file( generatePath( argv[0], "file.txt") );
ifstream in( file.c_str() );

So, thanks. The argv[0] solution seems to work great. (May break in some situations, but it works where I need it to)
 
Great! I'm glad you found a solution that you like.
 
^^ Thanks

However, since that works, now I would like to improve it if any. Have any suggestion?
 
Shadow2531 said:
However, since that works, now I would like to improve it if any. Have any suggestion?
None, aside from: quit usin' std::string. And for this application, it won't matter much.
 
mikeblas said:
None, aside from: quit usin' std::string. And for this application, it won't matter much.

O.K. thanks. I'll probably keep it like that then.

However, to be picky, could I improve the performance (not that it matters) if I did the function like this?:

Code:
#include <string>

using namespace std;

void generatePath(char p[], const string& file, string& store) {
    const string s( p );
    if ( s.find("/") != string::npos) {
        store += s.substr( 0, s.rfind( "/" ) ) ;
        store += "/";
        store += file;
    } else if ( s.find("\\") != string::npos ) {
        store += s.substr( 0, s.rfind( "\\" ) ) ;
        store += "\\";
        store += file;
    } else {
        store += file;
    }
}

int main(int argc, char* argv[] ) {
    string s;
    generatePath( argv[0], "file.txt", s);
}

For mingw, if I use the -S parameter to compile only, that method generates less instructions than the original. Also, I used += like that because

store += s.substr( 0, s.rfind( "/" ) ) ;
store += "/";
store += file;

generates less instructions than

store += s.substr( 0, s.rfind( "/" ) ) + "/" + file;

I'm going on the assumption that less instructions, means less things to do, which means better performance, but I don't know if I want to assume that.

I can post a comparison generated by mingw if you need more info. I have the vc++ tookit too, but don't know, which option generates assembly like mingw's -S option.
 
Nope, that's even worse for allocations and copies. (I think -- by one. Mabe it's the same; either way, it's no better.)

I thought I made this suggestion before: get out your debugger and step into the implementation of the string class. Step in to the substr function and operator+ to watch what they do and figure them out.

Less instructions doesn't mean faster. There's no correlation at all between instruction count and loops, in fact, because of loops and conditionals
 
mikeblas said:
I thought I made this suggestion before: get out your debugger and step into the implementation of the string class. Step in to the substr function and operator+ to watch what they do and figure them out.

I think you did, but I got sidetracked and forget to mention that I really don't know how to do that with gdb.

I can set breakpoints for functions like main() and can use "next" or 'step' to move to each line and can "print" out the value of a variable at a certain point, but I'm not sure how to have gdb show me what substr() is doing or += or + are doing. I can have gdb disassemble a function and I can check memory writes and reads.

However, I'm not sure what you mean by stepping through the string class.
 
I don't use gdb or ming, so I can't give specific advice.

When I install VC++, I always install the source code to the runtime libraries. That way, if something goes wrong in the runtimes because my code made a bogus call or corrupted something, then I can see the exact source in the runtimes that fell over when I crash.

While debugging in VC++, I can step over or step into. "Step over" means that I don't want to execute any function calls; the debugger will execute the function call but not step me through it. "Step Into" means that I do want to execute function calls and step through them.

I can "step into" any runtime library function I'd like, examine its code, learn what it is doing, and so on.

Not only does having the runtime sources help with debugging crashes, it's a very valuable too in discovering what each function really does. Tracing through the code in the string class means just that -- learn exactly what operator+ really is doing. Think about how much it costs compared to other things you could be doing with (or outside of) your use of std::string.
 
O.K. Thanks. I'm not sure if gdb will let me do that or not. I have the manuall for gdb so maybe that would help.

Sounds like I could look at \mingw\include\c++\3.4.2\bits\basic_string.h
It lists all the functions, operator+, operator+= etc.

operator+ and operator+= use append() ( at least partly, but you knew that )

example:

Code:
#include <string>

using namespace std;

int main() {
    string a( "a" );
    string b( "b" );
    string c( "c" );
    string total( a + b + c );
}

Basically, I think I'd get it more if I knew how the value for total is constructed.

I now think the following 2 would be equivalent to doing a + b + c :

Code:
#include <string>

using namespace std;

int main() {
    string a( "a" );
    string b( "b" );
    string c( "c" );
    string total( a );
    total += b;
    total += c;
}

or

Code:
#include <string>

using namespace std;

int main() {
    string a( "a" );
    string b( "b" );
    string c( "c" );
    string total( a );
    total.append( b );
    total.append( c );
}

However, it's not.

Operator+ and += still makes use of append(), which creates a temporary object or 2, but you're saying the first example,( a + b + c ) is better than the last two. That must mean when you have multiple +s like that, it must use append() less.
 
You can look at the header, but tracing through the code will be easier than static analysis.
 
How would I go about supporting paths with unicode characters in my function?

Code:
#include <iostream>
#include <string>
#include <fstream>

using namespace std;

inline wstring generatePath( const wchar_t* p, const wstring& file) {
    const wstring s( p );
    if ( s.find(L"/") != wstring::npos) {
        return s.substr( 0, s.rfind( L"/" ) ) + L"/" + file;
    } else if ( s.find(L"\\") != wstring::npos ) {
        return s.substr( 0, s.rfind( L"\\" ) ) + L"\\" + file;
    } else {
        return file;
    }
}

int wmain(int argc, wchar_t** argv) {
    if ( argc < 1 ) {
        return 1;
    }
    const wstring path( generatePath( argv[0], L"file.txt" ) );
    wifstream in( );
}

mingw doesn't support wifstream, wmain, wcout, wcout, wcerr, wofstream etc. so I'm trying this with the vc++ toolkit. (Not sure if gcc on *nix supports them either)

Anyway, the problem is wifstream wants a const char*, but I want it to accept a const wchar_t* and be able to do something like path.t_str() or something to that effect.

With that said, wifstream is not what I want to use I think because it doesn't accept unicode paths. I think I can conver to char* , but if I do that, I'll lose some of the characters.

I can create wifstream wcout etc. in mingw with the templates, but mingw still doesn't have wmain() and without it, argv[0] is blank. Plus there are errors.

(using wifstream because I want to read in unicode.)

Thanks

Edit:

I can create a new wchar_t* and copy the elements of the wstring to it and then use wcstombs to convert to a char*.

However, I'm not sure if wchar_t and a string of them are the right format. I *think* I want a 32bit char format. For example, the radic symbol is allowed in paths (in WinXP at least). However, the radic is a 3byte char, but wchar_t is only a 2 byte type. With a 32bit type, I think I'd be set if I can do something like wcstombs with a 32bit char type.

Edit: I can get wmain() in mingw if I --define UNICODE when I compile.
 
Shadow2531 said:
Anyway, the problem is wifstream wants a const char*, but I want it to accept a const wchar_t* and be able to do something like path.t_str() or something to that effect.
For which parameter on which function?
 
For example:

With ifstream you do:

const char* file = "file.txt";
ifstream in( file );

Now with wifstream, I want to do:

const wchar_t* file = L"file.txt";
wifstream in( file );

However, wifstream stilll wants a const char* for the constructor's file pointer paramater.

And, string has the .c_str() member, but wstring doesn't have a similar member.
 
Interesting. wifstream just makes the stream's charcter attribute wchar_t, not any of the parameters. I don't see a way to hand the stream a file descriptor, and no overrides that let you open a Unicode file with it.
 
Back
Top