Converting _TCHAR* to char*
Asked Answered
P

3

6

I'm trying to get a simple OpenCV sample working in C++ on Windows and my C++ is more than rusty.

The sample is fairly simple:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main( int argc, char** argv )
{
    if( argc != 2)
    {
        cout <<" Usage: display_image ImageToLoadAndDisplay" << endl;
        return -1;
    }
    Mat image;
    image = imread(argv[1], IMREAD_COLOR); // Read the file
    if(! image.data )                      // Check for invalid input
    {
        cout <<  "Could not open or find the image" << std::endl ;
        return -1;
    }
    namedWindow( "Display window", WINDOW_AUTOSIZE ); // Create a window for display.
    imshow( "Display window", image );                // Show our image inside it.
    waitKey(0); // Wait for a keystroke in the window
    return 0;
}

When I make a new simple C++ console application (with ATL) in Visual Studio 2012 I get a different template for main:

int _tmain( int argc, _TCHAR* argv[] )

So before I send the filename to OpenCV's imread function I need to convert the _TCHAR* arg[1] to a char*. Using a simple filename, 'opencv-logo.jpg', from the memory in the memory window I can see that the _TCHAR are taking two bytes each

o.p.e.n.c.v.-.l.o.g.o...j.p.g...
6f 00 70 00 65 00 6e 00 63 00 76 00 2d 00 6c 00 6f 00 67 00 6f 00 2e 00 6a 00 70 00 67 00 00 00

Following the conversion recommendation in another answer I am trying to use ATL 7.0 String Conversion Classes and Macros by inserting the following code:

char* filename = CT2A(argv[1]);

But the resulting memory is a mess, certainly not 'opencv-logo.jpg' as an ascii string:

fe fe fe fe fe fe fe fe fe fe ...
þþþþþþþþþþ ...

Which conversion technique, function, or macro should I be using?

(N.B. This may be a related question but I cannot see how to apply the answer here.)

Propositus answered 10/10, 2013 at 16:41 Comment(3)
Your project template is trying to create a C++ project for unicode support. I don't recall the specific project/compiler options, but there should be something to specify whether using unicode or not.Woodwork
Do you need a USES_CONVERSION; statement at the top of each function that makes use of the conversion macros?Rutaceous
It's in the general project properties pageDramshop
F
21

The quickest solution is to just change the signature to the standard one. Replace:

int _tmain( int argc, _TCHAR* argv[] )

With

int main( int argc, char *argv[] )

This does mean on Windows that the command line arguments get converted to the system's locale encoding and since Windows doesn't support UTF-8 here not everything converts correctly. However unless you actually need internationalization then it may not be worth your time to do anything more.

Fuegian answered 10/10, 2013 at 17:3 Comment(3)
I tried this - I'll edit the question to explain what (I think) went wrongPropositus
@Propositus Okay. Comment here when you've edited your question so I get a notification.Fuegian
@bames This does work. Last time I used the signature int _tmain( int argc, char** argv ) and the char* ended up as 6f 00 70 00 65 00 6e 00 63 00 76 00 2d 00 6c 00 6f 00 67 00 6f 00 2e 00 6a 00 70 00 67 00 00 00 which is the char c followed by \0 plus more bytes, i.e. the string "c"! Swapping _tmain to main makes all the difference, thank you.Propositus
E
7

_TCHAR, i.e. TCHAR is a type that depends on your project's settings. It can be either wchar_t (when you use Unicode) or char (when you use Multi-byte). You will find this in Project Properties - General, there's a setting Character Set.

Probably the simplest thing that you could do is just to use multi-byte option and treat _TCHAR* type as a simple char* and use it to construct std::string object ASAP:

std::string filename(argv[1]);

But in case you are going to work with special characters A LOT, then I find it more reasonable to use Unicode and hold strings in form of std::wstring objects wherever it's possible. If that's the case, then just use std::wstring's constructor instead:

std::wstring filename(argv[1]);

And in case you'll end up working with wide strings, sometimes you'll need a conversion between wide strings and multi-byte strings and these helpers might help you:

// multi byte to wide char:
std::wstring s2ws(const std::string& str)
{
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo(size_needed, 0);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

// wide char to multi byte:
std::string ws2s(const std::wstring& wstr)
{
    int size_needed = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), int(wstr.length() + 1), 0, 0, 0, 0); 
    std::string strTo(size_needed, 0);
    WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), int(wstr.length() + 1), &strTo[0], size_needed, 0, 0); 
    return strTo;
}
Edelmiraedelson answered 10/10, 2013 at 16:54 Comment(8)
"I recommend you using Unicode" Of course using Unicode doesn't preclude using char*. Typically I encode Unicode data as UTF-8 and stick with char*.Fuegian
@bames53: Depends on the context of program as well as APIs that he's going to use, but I admit that multi-byte seems to be a better choice here.Edelmiraedelson
@bames53: True, but unfortunately on windows unicode more or less implies utf-16 and wchar_t as that's what the win32 api uses.Dramshop
Thanks @Edelmiraedelson but I'm nut sure I understand this. What would I then pass to imread as its first argument?Propositus
@dumbledad: in case of std::string filename(argv[1]); you would pass filename.c_str()Edelmiraedelson
I'm getting an error from std::string filename(argv[1]); : error C2664: 'std::basic_string<_Elem,_Traits,_Alloc>::basic_string(const std::basic_string<_Elem,_Traits,_Alloc> &)' : cannot convert parameter 1 from '_TCHAR *' to 'const std::basic_string<_Elem,_Traits,_Alloc> &'Propositus
@dumbledad: Are you sure you are using multi-byte character set?Edelmiraedelson
Well spotted @LihO. I've changed the Character Set from Use Unicode Character Set to Use Multi-Byte Character Set in the General section of the Property Changes and now your std::string filename(argv[1]); with filename.c_str() work. Thanks!Propositus
R
-1

other than reading the 26,000 page cpp 2003 VS manual which hasn't changed much.... std::string test(print); test = "";

int _tmain( int argc, _TCHAR* argv[] )  

int main( int argc, char *argv[] ) 

work the same unless you were using some securitys function.... and couldn't be unicode in the character set property ... if you were to use the CreateFile function in file Management Functions unless you multi-threaded it somehow

Rouleau answered 11/12, 2017 at 6:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.