Making code (more) cross platform - c++

I am trying to make my (currently working on windows) code a bit more platform independent. This small snippet is for opening a text file (for reading/writing).
As with modern pcs many files are stored as unicode. Now In this question I am especially wondering what the right way to open such a file is. (Where the filename may be unicode).
On windows, using microsoft visual studio, I used
char* filename("c:\\\xD0\x92.txt");
#ifdef _WIN32
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
#else
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
//non windows wstring = utf8
#endif
std::wstring wfilename(converter.from_bytes(filename));
std::fstream fstream(wfilename);
However I just now realized this isn't standard C++11, instead a visual studio (only?) extension. However when I try on windows to first convert the widestring to an UTF-8 std::string and open the file that way the fstream doesn't recognize the filename.
So what is the cross platform solution? - Should I just expect std::fstream to always accept a widestring input? Or should I expect this on windows always?
And if possix systems don't use widestrings for their directories; what do they use?

Related

problem with compiling wstring on linux with cpprestsdk

I have code something like this
using namespace web;
using namespace http;
const http_response& response = /*valid assignment*/
http_headers::const_iterator it = response.headers().find(L"SomeKey");
if (it != response.headers().end())
{
//doing something
}
response is having valid data.
It is compiling with windows. I want to compile the same snippet in Linux with g++. How should I handle this?
Do I need to add some flags for compiling?
I got error like this:
error: no matching function for call to ‘web::http::http_headers::find(const wchar_t [17]) const’
http_headers::const_iterator it = response.headers().find(L"SomeKey");
^
The project use wstring for windows, string for Linux. And they provide a type string_t and a macro U to help deal with this, your code needs to be changed to be compiled both on Windows and Linux.
What is utility::string_t and the 'U' macro? The C++ REST SDK uses a
different string type dependent on the platform being targeted. For
example for the Windows platforms utility::string_t is std::wstring
using UTF-16, on Linux std::string using UTF-8. The 'U' macro can be
used to create a string literal of the platform type. If you are using
a library causing conflicts with the 'U' macro, for example
Boost.Iostreams it can be turned off by defining the macro
'_TURN_OFF_PLATFORM_STRING' before including the C++ REST SDK header
files.
See this FAQ
A tipical usage:
static const utility::string_t wl_birthday = U("wl.birthday");
static const utility::string_t wl_basic = U("wl.basic");
And for your code:
http_headers::const_iterator it = response.headers().find(U("SomeKey"));
WARN about the status of this project:
cpprestsdk is in maintenance mode and we do not recommend its use in new projects. We will continue to fix critical bugs and address security issues.

Read and write binary files with UTF-8 file names on windows (with QT) [duplicate]

This question already has answers here:
How to open an std::fstream (ofstream or ifstream) with a unicode filename?
(7 answers)
Closed 1 year ago.
I am trying to read and write to files with UTF-8 file names such as files with Arabic or Persian names ("سلام.jpg") in C++. I am using the following code and it works fine on *nix systems (I checked it up on Android NDK). But it fails on Windows. This code snippet just tries to read the length of the file via tellg but on Windows it tells that file isn't open and tellg returns -1. I am using QT MingW so the VS open doesn't apply for me.
QString inFileAdd)
this->inFileAdd=inFileAdd;
ifstream infile;
infile.open(this->inFileAdd.toUtf8().constData(),ios::binary|ios::in|ios::ate);
infile.seekg (0, infile.end);
cout<<"is open: ";
if(infile.is_open()){
cout<<"true"<<endl;
}else{
cout<<"false"<<endl;
}
this->size=infile.tellg();
cout<<this->inFileAdd.toUtf8().constData()<<endl<<"file Len:"<<this->size<<endl;
this->fileContent=nullptr;
infile.close();
ifstream (and ofstream) simply does not support UTF-8 file paths on Windows 1.
However, at least in Visual Studio, they have non-standard constructors (and open() overloads) that accept wchar_t* file paths, so you can convert your UTF-8 to UTF-16 (or, in your case, just use this->inFileAdd.toStdWString().c_str() instead) to open files that use Unicode paths/names.
1: Windows 10 has experimental support for UTF-8 as a user locale. Programs can manually opt-in to enable UTF-8 in ANSI-based APIs, like CreateFileA(), which ifstream/ofstream are likely to use internally.

non-ASCII file paths Windows

I work on Windows and have file paths with non-ASCII symbols. For non-ASCII symbols windows using wstring. I am doing the conversion and pass them to luaL_dofile but it fails with can not find a file.
Here is my example of code:
std::wstring wstr_path = "non-ASCII path"
using convert_type = std::codecvt_utf8_utf16<wchar_t>;
std::wstring_convert<convert_type, wchar_t> converter;
std::string str_path = converter.to_bytes(wstr_path);
luaL_dofile(mRoot, str_path.c_str());
I know nothing about luaL_dofile, but it's rather unlikely it uses UTF-8. Windows file API for Unicode unaware programs uses the ANSI codepage (which corresponds to the system default locale). The ANSI codepage on English/US systems is 1252, but other system default locales have different codepages. Central European is 1250, Cyrillic is 1251, etc.
Also, you could try generating the short name for the file (see the GetShortPathName API) and feed that.

Unicode output on windows console

The article Unicode apps in the MinGW-w64 wiki explains the following example for an Unicode application, e.g. _main.c_:
#define UNICODE
#define _UNICODE
#include <tchar.h>
int _tmain(int argc, TCHAR * argv[])
{
_tprintf(argv[1]);
return 0;
}
The above code makes use of tchar.h mapping, which allows it to both compile in Unicode and non-Unicode mode. [...] The -municode option is still required when linking if Unicode mode is used.
So I used
C:\> i686-w64-mingw32-gcc main.c -municode -o hello
_tmain.c:1:0: warning: "UNICODE" redefined
#define UNICODE
^
<command-line>:0:0: note: this is the location of the previous definition
to compile a Unicode application. But, when I run it, it returns
C:\> hello Süßer
S³▀er
So the Unicode string is wrong. I used the latest version 4.9.2 of MinGW-w64, i686 architecture and tried the Win32 and POSIX theads variants, both result in the same error. My operating system is 32-bit German Windows 7. When I used the Unicode codepage (chcp 65001), I have to use the font "Lucida Console". With this setting I get a similar error:
C:\> hello Süßer
S��er
I want to use a parameter with "ü" or "ß" in a Windows C++ program.
Solution
nwellnhof is right: The problem is the output on the console. This problem is explained in Unicode part 1: Windows console i/o approaches und Unicode part 2: UTF-8 stream mode. The latter gives a solution for Visual C++ - it worked also with Intel C++ 15. This blog post does "not yet consider the g++ compiler. All this code is Visual C++ specific. However, [the blog author has] done generally the same with g++, and [he] will probably discuss that in a third installment."
I want to open a file, which name is given by a parameter. This works simple, e. g. main.c:
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char* argv[])
{
if ( argc > 1 ) {
// The output will be wrong, ...
cout << argv[1] << endl;
// but the name of this file will be right:
fstream fl_rsl(argv[1], ios::out);
fl_rsl.close();
}
return 0;
}
and the compilation without unicode mode
C:\> g++ main.cpp -o hello && hello Süßer
It's console output is still wrong, but the created filename is right. This is okay for me.

Determine if Linux or Windows in C++

I am writing a cross-platform compatible function in C++ that creates directories based on input filenames. I need to know if the machine is Linux or windows and use the appropriate forward or back slash. For the following code below, if the machine is Linux then isLinux = true. How do I determine the OS?
bool isLinux;
std::string slash;
std::string directoryName;
if isLinux
slash = "/";
else
slash = "\\";
end
boost::filesystem::create_directory (full_path.native_directory_string() + slash + directoryName);
Use:
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
static const std::string slash="\\";
#else
static const std::string slash="/";
#endif
BTW, you can still safely use this slash "/" on Windows as windows understands this perfectly. So just sticking with "/" slash would solve problems for all OSes even like OpenVMS where path is foo:[bar.bee]test.ext can be represented as /foo/bar/bee/test.ext.
Generally speaking, you'd have do do this with conditional compilation.
That said, if you're using boost::filesystem you should be using the portable generic path format so that you can forget about things like this.
By default, Visual Studio #defines _WIN32 in the preprocessor project settings.
So you can use
// _WIN32 = we're in windows
#ifdef _WIN32
// Windows
#else
// Not windows
#endif
Look into http://en.wikipedia.org/wiki/Uname
If you are using g++ as your compiler/GNU then you could try the code below. POSIX compliant environments support this:
#include <stdio.h>
#include <sys/utsname.h>
#include <stdlib.h>
int main()
{
struct utsname sysinfo;
if(uname(&sysinfo)) exit(9);
printf("os name: %s\n", sysinfo.sysname);
return 0;
}
One of the most used methods to do this is with a pre-processor directive. The link is for C but they're used the same way in C++. Fair warning, each compiler & OS can have their own set of directives.
predef.sourceforge.net is a comprehensive collection of all kinds of MACROs that identify compilers/operating systems and more. (linking directly to the operating system sub-site)