1

I wish to be able to open a file in my Win32 application. The method I am using is retrieving the root path from an edit box e.g. "C:\MyFolder" (this is assigned to strPathToSource). Then I want to append another string e.g. "\source\Utilities\File.h" and store the concatenated path in a new variable strPathToFile.

So strPathToFile should contain "C:\MyFolder\source\Utilities\File.h", which can then be opened using infile.open(strPathToFile).

The relevant code is shown below:

ifstream infile;

int bufSize = 1024;
LPTSTR strPathToSource = new TCHAR[bufSize];
GetDlgItemText(hWnd, IDC_MAIN_EDIT_FILEPATH, strPathToSource, bufSize); // Get text from edit box and assign to strPathToSource

const char* strPathToFile = char(strPathToSource) + PATH_TO_FILE;
infile.open(strPathToFile);
if(!infile)
{
    log(hWnd, "File.h not found.");
    return false;
}

Where PATH_TO_FILE is defined as:

const char* PATH_TO_FILE = "\\source\\Utilities\\File.h";

My issue is that it is always logging out "File.h not found". I believe the issue lies with the concatenation e.g.

const char* strPathToFile = char(strPathToSource) + PATH_TO_FILE;

Stepping through I can see the values of strPathToSource and PATH_TO_FILE are as they should be, but the concatenated result in strPathToFile is a NULL value I believe.

petehallw
  • 1,014
  • 6
  • 21
  • 49
  • 3
    You don't concatenate pointers using `+`, and you don't mix `const char *` with `LPTSTR`, as they are different types. In other words, your code suffers from two (or more) issues, both unrelated but both can cause trouble. – PaulMcKenzie Feb 03 '16 at 14:09
  • Also -- **never** cast string types. If the compiler complains that the pointers are incompatible, do **not** cast away the error. Use the correct string types. – PaulMcKenzie Feb 03 '16 at 14:17
  • On Windows, it's best not to use `std::ifstream` at all. It's broken - by design - due to the missing constructor, taking a wide character string. If you still want to use `std::ifstream` on Windows, consider using Microsoft's extension, that provides a [basic_ifstream c'tor](https://msdn.microsoft.com/en-us/library/zek0beca.aspx) accepting a wide character string. – IInspectable Feb 03 '16 at 16:39

2 Answers2

2

Adding two character pointers does not concatenate the strings, it just adds the two pointer values (as "numbers"). So you end up with an invalid pointer.

Also, typecasting from LPTSTR to char* is not a good idea, as TCHAR can be wide character as well, depending on the current build settings. (in the fact, you are casting a LPTSTR to char, not to pointer, which is even more wrong)

I think the easiest would be to convert both strings to std::string (or wstring), there you can use the '+' operator to do the concatenation.

One possibility to do that:

const std::wstring strPathToFile = cvt2wstring(strPathToSource) + cvt2wstring(PATH_TO_FILE);

cvt2wstring defined as:

#include <codecvt>
#include <string>

std::wstring cvt2wstring(const char * str)
{
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t> > converter;
    return converter.from_bytes(str);
}

std::wstring cvt2wstring(const wchar_t * str)
{
    return str;
}

Depending on the actual type, it should choose the appropriate overload.

For the opposite conversion to std::string (so that you can use the regular std::ifstream) you can switch the direction:

std::string cvt2string(const char * str)
{
    return str;
}

std::string cvt2string(const wchar_t * str)
{
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t> > converter;
    return converter.to_bytes(str);
}

For me, the ifstream::open() seems to work with wstring (but it might be MSVC extension as the C++ standard does not provide that - anyway if you use TCHAR & comp. you probably target Windows and MSVC).

EmDroid
  • 5,918
  • 18
  • 18
  • 2
    If you are going to use TCHAR (I wouldn't. Pick one and stick with it - probably wchar_t these days), I strongly recommend defining a typedef `tstring` which is `std::basic_string`. That way you don't have to have ifdefs all over the place. – Martin Bonner supports Monica Feb 03 '16 at 14:19
  • Thanks for your answer. Could you please write how you would convert both to std::string or wstring? – petehallw Feb 03 '16 at 14:31
  • Note that the other issue with the code is that the `std::ifstream` takes only `char` based strings. That would also need to be typedefed to either `std::ifstream` or `std::wifstream`, depending on the build type. You should mention that in your answer. – PaulMcKenzie Feb 03 '16 at 14:50
  • Thank you for expanding on your answer. It seems that codecvt is not available for my compiler (I am using VS2008). If you know of a workaround to those conversion functions that would be great! I will accept the answer now. – petehallw Feb 03 '16 at 15:20
  • You can alternatively use mbstowcs() to convert char* to wstring as described here: http://stackoverflow.com/a/2573845/1274747 – EmDroid Feb 03 '16 at 15:50
  • "it just adds the two pointer values " - no, adding pointers is a type error. That means it's not a runtime error, but fails to compile. – MSalters Feb 03 '16 at 15:52
  • In this case it actually added char (as a number) to a char pointer, that seems to compile (but indeed produces a wrong result - basically it moves the pointer forwards or even backwards, depending on the typecasted value). – EmDroid Feb 03 '16 at 16:54
1

Martin Bonner basically gave the right answer.

In short, the best solution is to ignore TCHAR outright. It's a crutch dating back to the last decade of the previous century, when Unicode was a problem for PC's with 4 MB RAM. Today 4 GB is common, a thousand times more.

However, if you still want to live in that era, be consistent. Don't define

const char* PATH_TO_FILE = "\\source\\Utilities\\File.h";

but use

const TCHAR * PATH_TO_FILE = _T("\\source\\Utilities\\File.h");

Also, learn to use string concatenation. You can use std::basic_string<TCHAR>::operator+ (easy) or tcscat (get used to buffer overflows).

MSalters
  • 173,980
  • 10
  • 155
  • 350