I'm converting a float to a const wchar_t *
DisplayText(ConversionUtils::FloatToWstring(fps).c_str()); // Prints garbage
DisplayText(std::to_wstring(fps).c_str()); // Doesn't print anything to the device.
with this function :
std::wstring ConversionUtils::FloatToWstring(float value) {
return std::to_wstring(value);
}
I want to get something like that :
DisplayText(ConversionUtils::FloatToConstWcharPtr(fps));
Just return by value:
std::wstring ConversionUtils::FloatToWchar(float value) {
std::string str = std::to_string(value);
return std::wstring(str.begin(), str.end());
}
Or better, use std::to_wstring() instead.
Related
I was trying to get sf::String into std::filesystem::u8path. My first method is converting it into an std::string, (std::string)sfstringbar but it sees it as a single byte character, i also tried auto x = sfstringbar.toUtf8() std::string(x.begin(), x.end()) but the same. My second method is to pass it as a char array which hopefully could read it with the UTF 8 encoding, but still the same happens.
EDIT:
char* makeutf8str(str string) {
std::basic_string<sf::Uint8> utf8 = string.toUtf8();
std::vector<char>* out = new std::vector<char>;
for (auto x = utf8.begin(); x != utf8.end(); x++) {
out->push_back(*x);
}
return &(out->at(0));
}
bool neaxfile::isfile(str file) {
std::cout << "\nThis: " << makeutf8str(file) << "\n";
return std::filesystem::is_regular_file(std::filesystem::u8path(makeutf8str(file)));
}
Here's about the second solution i tried. I have a file called Яyes.txt as an example, but when i pass in to check if it exists, it says it doesn't. Because the makeutf8str() function splits Я into Ð and ¯. I can't seem to get the encoder to work properly.
EDIT 2:
str neaxfile::getcwd() {
std::error_code ec;
str path = std::filesystem::current_path(ec).u8string();
if (ec.value() == 0) {
return path;
} else {
return '\0';
}
}
std::vector<str> neaxfile::listfiles() {
std::vector<str> res;
for (auto entry : std::filesystem::directory_iterator((std::string)neaxfile::getcwd())) {
if (neaxfile::isfile(entry.path().wstring())) res.push_back(entry.path().wstring());
}
return res;
}
I tried the first solution below. It no longer prints Я. But it still doesn't confirm that this is a file. I tried to list the files using that ^
std::filesystem::u8path() "Constructs a path p from a UTF-8 encoded sequence of chars [or char8_ts (since C++20)], supplied either as an std::string, or as std::string_view, or as a null-terminated multibyte string, or as a [first, last) iterator pair."
A std::string can hold a UTF-8 encoded char sequence (better to use std::u8string in C++20, though). sf::String::ToUtf8() returns a UTF-8 encoded std::basic_string<Uint8>. You can simply cast the UInt8 data to char to construct a std::string, there is no need for your makeutf8str() function to use std::vector<char> or return a raw char* at all (especially since it is leaking the std::vector anyway).
You can use the std::string constructor that takes a char* and a size_t as input, eg:
std::string makeutf8str(const str &string) {
auto utf8 = string.toUtf8();
return std::string(reinterpret_cast<const char*>(utf8.c_str()), utf8.size());
}
Or, you can use the std::string constructor that takes a range of iterators as input (despite your claim, this should work just fine), eg:
std::string makeutf8str(const str &string) {
auto utf8 = string.toUtf8();
return std::string(utf8.begin(), utf8.end());
}
Either way will work fine with std::cout and std::filesystem::u8path(), eg:
bool neaxfile::isfile(const str &file) {
auto utf8 = makeutf8str(file);
std::cout << "\nThis: " << utf8 << "\n";
return std::filesystem::is_regular_file(std::filesystem::u8path(utf8));
}
That being said, the Unicode character Я is encoded in UTF-8 as bytes 0xD0 0xAF, which when interpreted as Latin-1 instead of UTF-8 will appear as Я. This means the std::string data is properly UTF-8 encoded, it is just not being processed correctly. For instance, if your console cannot handle UTF-8 output, then you will see Я instead of Я. But, u8path() should process the UTF-8 encoded std::string just fine, and convert it to the filesystem's native encoding as needed. But then, there is no guarantee that the underlying filesystem will actually handle a Unicode filename like Яyes.txt properly, but that would be an OS issue, not a C++ issue.
UPDATE: your listfiles() function is not making use of UTF-8 at all when using directory_iterator. It is type-casting the sf::String from getcwd() to an ANSI encoded std::string (which is a lossy conversion), not to a UTF-8 encoded std::string. But worse, that sf::String is being constructed by getcwd() from a UTF-8 encoded std::string but the std::string constructor of sf::String requires ANSI by default, not UTF-8 (to fix that, you have to give it a UTF-8 std::locale). So, you are passing through several lossy conversions trying to get a string from the std::filesystem::pathreturned fromstd::filesystem::current_pathtostd::filesystem::directory_iterator`.
sf::String can convert to/from std::wstring, which std::filesystem::path can also use, so there is no need to go through UTF-8 and std::filesystem::u8path() at all, at least on Windows where std::wstring uses UTF-16 and Windows underlying filesystem APIs also use UTF-16.
Try this instead:
bool neaxfile::isfile(const str &file) {
std::wstring wstr = file;
std::wcout << L"\nThis: " << wstr << L"\n";
return std::filesystem::is_regular_file(std::filesystem::path(wstr));
}
str neaxfile::getcwd() {
std::error_code ec;
str path = std::filesystem::current_path(ec).wstring();
if (ec.value() == 0) {
return path;
} else {
return L"";
}
}
std::vector<str> neaxfile::listfiles() {
std::vector<str> res;
std::filesystem::path cwdpath(neaxfile::getcwd().wstring());
for (auto entry : std::filesystem::directory_iterator(cwdpath) {
str filepath = entry.path().wstring();
if (neaxfile::isfile(filepath)) res.push_back(filepath);
}
return res;
}
If you really want to use UTF-8 for conversions between C++ strings and SFML strings, then try this instead to avoid any data loss:
std::string makeutf8str(const str &string) {
auto utf8 = string.toUtf8();
return std::string(reinterpret_cast<const char*>(utf8.c_str()), utf8.size());
}
str fromutf8str(const std::string &string) {
return str::fromUtf8(utf8.begin(), utf8.end());
}
bool neaxfile::isfile(const str &file) {
auto utf8 = makeutf8str(file);
std::cout << "\nThis: " << utf8 << "\n";
return std::filesystem::is_regular_file(std::filesystem::u8path(utf8));
}
str neaxfile::getcwd() {
std::error_code ec;
auto path = std::filesystem::current_path(ec).u8string();
if (ec.value() == 0) {
return fromutf8str(path);
} else {
return "";
}
}
std::vector<str> neaxfile::listfiles() {
std::vector<str> res;
auto cwdpath = std::filesystem::u8path(makeutf8str(neaxfile::getcwd()));
for (auto entry : std::filesystem::directory_iterator(cwdpath)) {
str filepath = fromutf8str(entry.path().u8string());
if (neaxfile::isfile(filepath)) res.push_back(filepath);
}
return res;
}
That being said, you are doing a lot of unnecessary conversions between C++ strings and SFML strings. You really shouldn't be using SFML strings when you are not directly interacting with SFML's API. You really should be using C++ strings as much as possible, especially with the <filesystem> API, eg:
bool neaxfile::isfile(const std::string &file) {
std::cout << L"\nThis: " << file << L"\n";
return std::filesystem::is_regular_file(std::filesystem::u8path(file));
}
std::string neaxfile::getcwd() {
std::error_code ec;
std::string path = std::filesystem::current_path(ec).u8string();
if (ec.value() == 0) {
return path;
} else {
return "";
}
}
std::vector<std::string> neaxfile::listfiles() {
std::vector<std::string> res;
auto cwdpath = std::filesystem::u8path(neaxfile::getcwd());
for (auto entry : std::filesystem::directory_iterator(cwdpath)) {
auto filepath = entry.path().u8string();
if (neaxfile::isfile(filepath)) res.push_back(filepath);
}
return res;
}
Alternatively:
bool neaxfile::isfile(const std::wstring &file) {
std::wcout << L"\nThis: " << file << L"\n";
return std::filesystem::is_regular_file(std::filesystem::path(file));
}
std::wstring neaxfile::getcwd() {
std::error_code ec;
auto path = std::filesystem::current_path(ec).wstring();
if (ec.value() == 0) {
return path;
} else {
return L"";
}
}
std::vector<std::wstring> neaxfile::listfiles() {
std::vector<std::wstring> res;
std::filesystem::path cwdpath(neaxfile::getcwd());
for (auto entry : std::filesystem::directory_iterator(cwdpath)) {
auto filepath = entry.path().wstring();
if (neaxfile::isfile(filepath)) res.push_back(filepath);
}
return res;
}
A better option is to simply not pass around strings at all. std::filesystem::path is an abstraction to help shield you from that, eg:
bool neaxfile::isfile(const std::filesystem::path &file) {
std::wcout << L"\nThis: " << file.wstring() << L"\n";
return std::filesystem::is_regular_file(file);
}
std::filesystem::path neaxfile::getcwd() {
std::error_code ec;
auto path = std::filesystem::current_path(ec);
if (ec.value() == 0) {
return path;
} else {
return {};
}
}
std::vector<std::filesystem::path> neaxfile::listfiles() {
std::vector<std::filesystem::path> res;
for (auto entry : std::filesystem::directory_iterator(neaxfile::getcwd())) {
auto filepath = entry.path();
if (neaxfile::isfile(filepath)) res.push_back(filepath);
}
return res;
}
I want my function() to always return a "" string under error conditions else return a string that is converted to string from an unsigned long integer variable.
My initial implementation is as follows:
uint32 cfgVariable_1 = 4;
uint32 cfgVariable_2 = 1;
const char* getCfgVariable (const char* msg)
{
char* retValue = "";
if(strcmp("cfgVariable_1", msg)==0)
{
// Since I want my function to return a const char* and returning uint32_t is not an option
sprintf((retValue), "%lu", cfgVariable_1);
return (const char*)retValue;
}
else if(strcmp("cfgVariable_2", msg)==0)
{
// Since I want my function to return a const char* and returning uint32_t is not an option
sprintf((retValue), "%lu", cfgVariable_2);
return (const char*)retValue;
}
else
{
//error
}
return (const char*) retValue;
}
When the function is called at different instances to get the cfgVariables, I expect my function getCfgVariable() to return "" on error condition, when no match found.
Somewhere in code:
const char* CfgValue = NULL;
CfgValue = getCfgVariable("cfgVariable_1");
Here CfgValue gets pointed to location which contains 4
later
const char* CfgValue = NULL;
CfgValue = getCfgVariable("cfgVariable_3");
I expect to get a "" back but I get 4 instead (CfgValue gets the same address as before).
Fix implemented by me works, but I fail to understand the logic behind it, fix:
const char* getCfgVariable (const char* msg)
{
const char* defValue = "";
char* retValue = "\0";
if(strcmp("cfgVariable_1", msg)==0)
{
// Since I want my function to return a const char* and returning uint32_t is not an option
sprintf((retValue), "%lu", cfgVariable_1);
return (const char*)retValue;
}
else if(strcmp("cfgVariable_2", msg)==0)
{
// Since I want my function to return a const char* and returning uint32_t is not an option
sprintf((retValue), "%lu", cfgVariable_2);
return (const char*)retValue;
}
else
{
//error
}
return defValue;
}
I see during debugging that defValue and retValue get pointed to two different locations that do not get overwritten. defValue always gets pointed to the same address when its initialized with "" and retValue gets pointed to a different address when initialized with "\0". Can anyone explain the logic behind this ? Is there a better implementation for my use case ?
My Solution after considering the comments:
const char* getCfgVariable (const char* msg)
{
const char* retValue = "";
std::ostringstream oss;
if(!strcmp("cfgVariable_1", msg))
{
oss << cfgVariable_1;
}
else if(!strcmp("cfgVariable_2", msg))
{
oss << cfgVariable_2;
}
else
{
//error
return retValue;
}
const std::string tmp = oss.str();
retValue = tmp.c_str();
return retValue;
}
Thanks for the comments so far and this solution is still open to further improvement suggestions.
Constexpr strings such as "\0", "", "cfgVariable_1", etc. These are constant strings in memory compiled into your resulting executable. Attempting to write values into those strings is downright dangerous! In old style C, you'd have to use malloc to allocate a bit of memory to use for your string. This is a real pain to deal with in practice (and not ideal for someone who's learning C++).
A far simpler solution is to start using the C++ string object, std::string (which handles all of the dynamic memory allocation for you!). This should reduce your problem to something a little simpler (and most importantly, safer!):
#include <string>
#include <sstream>
std::string getCfgVariable (const char* const msg)
{
std::ostringstream oss;
if(!strcmp("cfgVariable_1", msg))
{
oss << cfgVariable_1;
}
else
if(!strcmp("cfgVariable_2", msg))
{
oss << cfgVariable_2;
}
return oss.str();
}
Doing this in C, you have 2 choices. Allocate the memory for the returned string, or use a static buffer that is always available (which is what this example does).
uint32 cfgVariable_1 = 4;
uint32 cfgVariable_2 = 1;
const char* getCfgVariable (const char* msg)
{
static char retValue[32] = {0};
if(strcmp("cfgVariable_1", msg)==0)
{
// Since I want my function to return a const char* and returning uint32_t is not an option
sprintf(retValue, "%lu", cfgVariable_1);
return retValue;
}
else if(strcmp("cfgVariable_2", msg)==0)
{
// Since I want my function to return a const char* and returning uint32_t is not an option
sprintf(retValue, "%lu", cfgVariable_2);
return retValue;
}
else
{
//error
}
return retValue;
}
However, because now the retValue is an array fixed in memory, the string returned would only be valid until the next call to getCfgVariable, which could be a little strange....
const char* A = getCfgVariable("cfgVariable_1");
printf("%s\n", A); // prints '4'
const char* B = getCfgVariable("cfgVariable_2");
printf("%s\n", B); // prints '1'
printf("%s\n", A); // now this will print '1', and not '4'.
const char* C = getCfgVariable("anythingElse");
printf("%s\n", C); // prints ""
printf("%s\n", B); // prints ""
printf("%s\n", A); // aso prints ""
I use 2 method for these conversion
// Vector to String.
VectorToString(std::vector<char> data)
{
const char* newData = &data[0];
String ^result;
result = marshal_as<String^>(newData);
return result;
}
// String to vector
StringToVector(String ^ data)
{
marshal_context ctx;
IntPtr p = Marshal::StringToHGlobalAnsi(data);
const char* pAnsi = static_cast<const char*>(p.ToPointer());
// use pAnsi
std::vector<char> result;
result.assign(pAnsi, pAnsi + strlen(pAnsi));
Marshal::FreeHGlobal(p);
return result;
}
with 2 above function I can doing convert.
can you tell me these conversion is correct? or not?
actually, this way for convert std::vector to String is best way?
you must be add #include msclr/marshal_cppstd.h
vector to String ^
vector<char> data;// this is be initialize
std::string myString = std::string(begin(data), end(data));
String^ result = marshal_as<String^>(myString);
string ^ to vector
marshal_context context;
std::vector<char> myVector;
const char* afterConvert = context.marshal_as<const char*>(data);
myVector.assign(afterConvert , afterConvert + strlen(afterConvert));
I need to take an std::string that I have and convert it to a WCHAR does anyone know how to do this? Any help is appreciated
Your question is vague; wchar_t is used to store a wide character, and wstring is to store a wide string. You can't convert a string to wchar_t.
But if your aim was to convert an std::string to wchar_t*, then you need to convert your std::string to an std::wstring first, then to convert your std::wstring to const wchar_t*.
string narrow_string("A string");
wstring wide_string = wstring(narrow_string.begin(), narrow_string.end());
const wchar_t* result = wide_string.c_str();
Assuming you want to convert from the locale encoding, since you want wchar_t.
Option 1, since C++11 but deprecated in C++17:
// See https://stackoverflow.com/questions/41744559
template<class I, class E, class S>
struct codecvt_ : std::codecvt<I, E, S> { ~codecvt_ () {} };
std::wstring to_wide (const std::string &multi) {
return std::wstring_convert<codecvt_<wchar_t, char, mbstate_t>> {}
.from_bytes (multi);
}
Option 2, widely portable:
std::wstring to_wide (const std::string &multi) {
std::wstring wide; wchar_t w; mbstate_t mb {};
size_t n = 0, len = multi.length () + 1;
while (auto res = mbrtowc (&w, multi.c_str () + n, len - n, &mb)) {
if (res == size_t (-1) || res == size_t (-2))
throw "invalid encoding";
n += res;
wide += w;
}
return wide;
}
#define printm(p) writeToAnalyticsFile p
void writeToAnalyticsFile (const char* format, ...)
{
std::string errorLog;
std::string headerLine = "\"errorText\":";
FILE *fp = fopen( "Analyse.txt", "w" );
if( 0 == fp )
{
}
else
{
va_list args;
va_start (args, format);
vsprintf (const_cast<char*>(errorLog.c_str()),format, args);
va_end (args);
fputs(headerLine.c_str(),fp);
fputs("\"",fp);
fputs( errorLog.c_str(), fp );
fputs("\"",fp);
fclose(fp);
}
}
class abc
{
public:
static const char * func()
{
std::string str = "john";
return str.c_str();
}
};
int main()
{
printm(("My Name is %s and surname is %s and age is %d",abc::func(),"john",25));
return 0;
}
I basically want to replace printm((--- )) with writeToAnalytics(--) which writes data to file.
I am getting Segmentation Fault with below code.
Problem is with calling abc::func() in main. Is it because of local storage of str in func()?
The func function is incorrect, as it is returning an invalid pointer:
static const char * func()
{
std::string str = "john";
return str.c_str();
}
The string is destroyed at the end of the function, and therefore the pointer str.c_str() is a dangling pointer - pointing to a string that doesn't exist any more.
Solution is to just return "john". Literals last forever.
static const char * func()
{
return "john";
}
Or, better still, just return the string,
static string func()
{
std::string str = "john";
return str;
}
and use abc::func().c_str() at the call site.
(There may be other problems, but this is the most obvious to me)