How to construct WebURL from std::wstring? - c++

I am writing a Win32 application with Awesomium. According to the tutorial section I can load a local file inside my view:
WebURL url(WSLit("file:///C:/dev/project/util/ui/index.html"));
view_->web_view()->LoadURL(url);
This works as expected.
When I try to pass an std::wstring to WSList function:
std::wstring ui_path = L"file:///" + install_path + L"/util/ui/index.html";
WebURL url(WSLit(ui_path));
view_->web_view()->LoadURL(url);
I get a compiler error:
src/main.cc(52) : error C2664: 'Awesomium::WSLit' : cannot convert parameter 1 from
'std::wstring' to 'const char *' No user-defined-conversion operator available that can
perform this conversion, or the operator cannot be called
When I try to pass WSLit( ui_path.c_str() ) the compiler throws another error:
src/main.cc(52) : error C2664: 'Awesomium::WSLit' : cannot convert parameter 1 from
'const wchar_t *' to 'const char *'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or
function-style cast
What is the right way to convert it to const char * type?
Edit:
From Awesomium documentation :
WSLit() is a special helper function that lets you declare WebString literals. Most of our API uses UTF-16 strings (wrapped with WebString) but we added WSLit() so you can declare ASCII C-strings with minimal fuss.

WSLit is meant to construct a WebString object from an ASCII string. Since you don't want to construct a WebString from ASCII, but rather have a UNICODE string from the beginning, you simply don't need to use WSLit at all.
The following line of code constructs a WebURL from a std::wstring:
WebURL url(WebString(ui_path.c_str()));
As pointed out by Remy Lebeau this may not compile for any given compiler or compiler settings. WebString has an explicit constructor taking a const wchar16*. Platform.h defines wchar16 as
typedef unsigned short wchar16;
Depending on your compiler and compiler settings, this may or may not be the same as wchar_t. When compiling with the Microsoft compiler using the command line option /Zc:wchar_t, wchar_t is interpreted as a native data type. This is a different type from unsigned short, and the explicit constructor of WebString requires an additional cast:
WebURL url(WebString(reinterpret_cast<const wchar16*>(ui_path.c_str())));
Implicitly invoking the conversion constructor of WebString using the following syntax is not possible, since it is declared explicit:
WebURL url(reinterpret_cast<const wchar16*>(ui_path.c_str()));

If the question is "convert std::wstring to const char*", I usually use this function and it works fine:
std::string wstringToString(const std::wstring& in){
std::string result(in.begin(), in.end());
return result;
}
And then, you can get char* by calling result.c_str();

Try this one:
std::wstring ui_path = L"file:///" + install_path + L"/util/ui/index.html";
std::string cui_path( ui_path.begin(), ui_path.end() );
WebURL url(WSLit(cui_path));
view_->web_view()->LoadURL(url);
However, IMHO, if Awesomium is intended to run under Windows, the WSLit constructor should support either a std::wstring or wchar_t * argument.

Related

Cannot construct CString from a char

In VS2008, I try to construct a CString from a char, as follows:
CString str = CString(_T('A') + 5);
But will get the following compile error:
error C2440: '' : cannot convert from 'int' to 'CString'
I belive TCHAR and int is exchangable in C++, so why it refuses to accept the TCHAR type parameter to construct a CString?
The purpose of the line is to construct a CString with only one character _T('F'). Not to get "A5".
I try to understand how the compiler processes the code, as follows:
It will first promote _T('A') to an integer.
It will add the integer value of _T('A') by 5.
It will get a new integer and cannot find a way to convert it back to TCHAR, which can be used as input parameter for CString constructor.
The CstringT constructors that accept a char or a wchar_t are explicit. See the docs.
The explicit specifier in C++ means that the compiler is not allowed to do an implicit conversion of the parameter(s). See also explicit specifier.
Thus the compiler cannot (is not allowed to) implicitly convert the int value to char or wchar_t.
(Note that this depends on the flag _ATL_CSTRING_EXPLICIT_CONSTRUCTORS being set. Without the flag being set, the constructors are not explicit, but then it's ambiguous which constructor to chose.)
So you need to explicitly cast the parameter to char or wchar_t, depending on what you want:
CStringA str = CStringA(static_cast<char>('A' + 5));
or:
CStringW str = CStringW(static_cast<wchar_t>(L'A' + 5));
CString will not implicitly convert an int to CString. However you can try like so
CString str;
char c = 'A';
int i = 5;
str.Format(L"%c%d",c, i); // unicode assumed

C++ convert std::string to char

I have following scenario:
struct MyStruct
{
char name[25];
char address[12];
}
Somewhere::SomeMethod(std::shared_ptr<SomeArgumentClass> args)
{
// GetName() and GetAddress() both return std::strings
::MyStruct newValue = {args->GetName(), args->GetAddress()};
// add newValue to some structure that needs to have chars
}
Error:
error C2440: 'initializing' : cannot convert from 'std::string' to 'char'
error C2440: 'initializing' : cannot convert from 'std::string' to 'char'
I am not able to get my std::string converted to a char.
Thanks a lot!
Firstly, your terminology is incorrect. You don't want to convert to char, which is a single character. I realise that the error message claims you're trying to convert to char, but that's because your incorrect code has confused the compiler into thinking you're trying to initialise individual elements of the array name. Be wary of the specifics of an error message when you write the wrong code, because the compiler cannot read your mind — it can only go off of what you've written!
In reality you want to copy the characters inside your std::string, into the array of char, that is your member.
Like so:
Somewhere::SomeMethod(std::shared_ptr<SomeArgumentClass> args)
{
const auto& name = args->GetName();
const auto& address = args->GetAddress();
::MyStruct newValue;
std::copy(std::begin(name), std::end(name), &newValue.name[0]);
std::copy(std::begin(address), std::end(address), &newValue.address[0]);
// add newValue to some structure that needs to have chars
}
But you need to add bounds checking too. To do that, roughly I might consider replacing each std::copy call with something like:
std::copy_n(
&name[0],
std::min(name.size(), sizeof(newValue.name)),
&newValue.name[0]
);
You'll also need some null termination depending on how many characters were copied.
Generally though the salient point is that you need to actually copy those characters because there is no one-step way to do it. It stems in part from how arrays can't be assigned-to, but also from the fact that their dimensions are part of the type which makes things more complicated.
If this seems like a pain in the arse, that's because it is — ideally you would stick with std::string across the board because it's far superior, and was specifically introduced to make it easier to handle arrays of char. You can still pass a const char* (or char*!) pointing to the string's data into a C API. Of course if ::MyStruct itself is a third-party C type, you have no choice but to do it the hard way.

C++ error: cannot convert ‘std::basic_string<char>’ to ‘const char*’

I'm using a function to download a file.
void downloadFile(const char* url, const char* fname) {
//..
}
This is called like :
downloadFile("http://servera.com/file.txt", "/user/tmp/file.txt");
This working fine.
But I want to change the URL to be a value from an array. The array stores encrypted values which when decrypted are strings, so I get the issue error: cannot convert ‘std::basic_string<char>’ to ‘const char*’
I've tried:
string test = decode(foo[5]);
const char* t1= test.c_str();
downloadFile(t1 "filename.txt", "/user/tmp/file.txt");
downloadFile(t1 + "filename.txt", "/user/tmp/file.txt");
and
downloadFile((decode(foo[5]).c_str()) + "filename.txt", "/user/tmp/file.txt");
which gives:
error: invalid operands of types ‘const char*’ and ‘const char [17]’ to binary ‘operator+’
What am I doing wrong ?
Thanks
C-strings can't be concatenated with +.
Use std::string::+ instead:
downloadFile((test + "filename.txt").c_str(), "/user/tmp/file.txt");
Note that c_str only returns a pointer to the std::string's internal character array, so it's valid only during the execution of the downloadFile function.
Try this:
downloadFile((decode(foo[5]) + "filename.txt").c_str(), "/user/tmp/file.txt");
The operator+ is not defined for char arrays.
The main problem in your code is that you are trying to use operator+ to concatenate raw C strings (i.e. raw const char* pointers, or raw char [] arrays), which doesn't work.
In C, you should use proper library functions (like strncat or safer variants) to do that; but since you are using C++, you can do better, and write easier code: just use a C++ string class, like std::string.
In fact, the C++ standard library offers convenient overloads for operator+ that work with std::string, so you can concatenate C++ strings in an easy, intuitive and safe way; for example:
// Build your URL string
std::string test = decode(foo[5]);
std::string url = test + "filename.txt";
// Use std::string::c_str() to convert from C++ string
// to C raw string pointer const char*
downloadFile(url.c_str(), "/user/tmp/file.txt");

cannot convert from 'const char [3]' to 'char *' x100000 (Qt Creator C++ Windows 32)

Everything was working fine just five minutes ago when I tapped f5 and got 102 errors:
error: C2440: 'initializing' : cannot convert from 'const char [17]' to 'char *'
Conversion from string literal loses const qualifier (see /Zc:strictStrings)
That specific one is at line 30:
char* hexchars = "0123456789ABCDEF";
I haven't touched the file the errors are in for at least a week. I'd normally say I accidentally changed something in the compile args or something, but I haven't opened settings since much before it started erroring.
Any ideas? I must have absentmindedly changed some setting but I really can't remember thinking "uh oh what did I just do?"
When you use code like this
char *astring2 = "some letters";
C++ (and C) puts that into read only memory. You can not modify the contents of a char pointer initialized with a literal even if it is not const.
Also you can not change the address of the pointer because it will cause a memory leak due to the rule above.
This, however, does not follow that rule UNLESS you make it const:
char astring[] = "some letters that can be changed";
char *ptrToString = astring; //work
astring2 = astring //not work
String literals are of type char const[N] since C++ was first standardized. At this point C didn't support const and a lot of code assigned string literals to char*. As a result a special rule was present in C++ which allowed initialization of char* from string literals. This rule was immediately deprecated.
C99 introduced a const keyword, too. When C++11 was standardized the deprecated rules was pulled and it is no illegal to initialize a char* from a string literal as it should have been right from the stand. The expectation was that C++ compilers warned about the deprecated assignment since years (and all vendors stated they did), i.e., users had years of lead-time to fix their code.
The obvious fix is to initialize a char const* instead of a char* from a string literal.
If you really need a pointer to a mutable array of chars you can create it and get it initialized using
char array[] = "string literal";

Setting CHAR16* to a #define string?

I'm a little confused on the proper way to do this. I have a #DEFINE that contains a string, and I have a CHAR16* that I want to set to that string. How would I properly do this? I've tried:
#DEFINE MYSTRING "HELLO"
CHAR16* THISONE;
THISONE = MYSTRING;
Why won't this work? I want to be able to print out the string in THISONE. I get a compiler warning regarding incompatible types. I'm sure I'm missing something small?
You're defining a narrow string literal "HELLO", but trying to use a CHAR16 pointer to point to it. That's not a compatible assignment. As clang says:
example.cpp:9:13: error: assigning to 'wchar_t *' from incompatible type
'const char [6]'
THISONE = MYSTRING;
^ ~~~~~~~~
1 error generated.
(I changed the type from CHAR16 * to wchar_t * since I'm not on windows - the semantics are the same).
To fix it, you need to add an L in front of the string constant:
#define MYSTRING L"HELLO"
And then it will compile. In C, you're done. In C++, however, you will probably still get a warning:
example.cpp:9:15: warning: conversion from string literal to 'wchar_t *' is
deprecated [-Wdeprecated-writable-strings]
THISONE = MYSTRING;
^
example.cpp:3:18: note: expanded from macro 'MYSTRING'
#define MYSTRING L"HELLO"
^
1 warning generated.
Change the definition to:
const wchar_t *THISONE;
To fix that warning. I guess in your case that would be:
const CHAR16 *THISONE;
Editorial note - in the future, please show your real code. #DEFINE (with the capital letters) isn't valid C or C++.
You probably want something like:
// Update: As carl points out, there is more to this for wide chars
const CHAR16* THISONE = MYSTRING;
You can explore more about c-strings here and also from within this very site.
You probably want to assign it as a global variable. In this case you need:
#define MYSTRING "HELLO"
CHAR* THISONE = MYSTRING;
or
#define MYSTRING L"HELLO"
CHAR16* THISONE = MYSTRING;
If assignment is in function (main() or other) you can make assignment not at the same place as variable creation.
Actually, I'm not sure if there is such a type "CHAR16"