4 ms·
Yes, the cross-platform stuff sucks on Windows. I’ve seen languages with a few different approaches, I’d like a moment to compare them here. I’m going to talk a
by klodolph 5y ago
Yes, the cross-platform stuff sucks on Windows. I’ve seen languages with a few different approaches, I’d like a moment to compare them here. I’m going to talk about some specific aspects of cross-platform compatibility but not attempt to compare one single aspect across many platforms.
With C++, you can use the preprocessor to give you a string type which is UTF-8 (nominally) when compiled on Unix and UTF-16 (nominally) on Windows. This is… okay-ish, workable, but I’m unaware of any good libraries that do this for you. Instead, you’re basically on your own. It’s not horrible, it’s not great, you spend some time writing interfaces to work on Windows (if you are doing anything specific). There’s no C++ “mkdir” call, for example. (Maybe there is in C++75 or whatever. They keep adding things. There was no “mkdir” in C++ for, like, 30 years.)
As a side note, there is something in C++ called “wchar_t” and “std::wstring” which is basically complete garbage and only ends up being used because somebody, in the past, wrote code that used it and now you are stuck with it. There’s a whole story here.
With C# and .NET, the APIs seem to be designed with Windows as the norm, and Unix/Unix-like systems are an afterthought with some shim for portability. You can take a look at System.Diagnostics.Process for one of the worst offenders. Anything involving pipes is just not doable with the .NET process spawning interface, as far as I can tell, because it wouldn’t match the Windows semantics. You also can’t even roll your own process spawner without great difficulty, because you can’t safely call fork() from C# (not a surprise).
With Go, the APIs are designed with Unix as the norm, as discussed in the article.
With Rust, the APIs are carefully designed to give you a subset of functionality that is present on both Windows and Unix. As a result, the available functionality is (IMO) garbage, kind of like wstring in C++. In short, with OsString / OsStr in Rust, you get a string that is hard to manipulate. In the effort to make it safe and cross-platform, it’s been made into something like an opaque box for strings, and it’s missing 90% of the typical API that you’d find on a useful string class. In an effort to make sure that you don’t pay any unnecessary cost for conversion to/from Rust strings, the subset of OsString that are valid Unicode strings can be typecast at zero cost to Rust String / &str types—but this means that the encoding of these strings doesn’t match the Windows encoding at all.
F'ing bizarre.
Honestly I do not think that there is any easy out here, at all. If you care about running on Windows, and you care about providing a good experience on both Unix-like and Windows systems, then you should embrace the fact that any cross-platform API that abstracts the differences away will be imperfect, and just ask yourself what tradeoff you think is appropriate to get a better experience on Windows and Unix-like systems.
I’ll also add that while macOS provides a fully functional, fully usable POSIX API, that doesn’t mean that this API is the best option for performing low-level tasks on macOS. In particular, you should probably be going through the NSFileManager API when possible if you really care about macOS, because this provides higher-level functionality that is tricky to implement yourself—the “correct” way to do certain operations is different depending on the semantics of the underlying filesystem.
I’m just going to finish with the note that if you want to handle the pathological edge cases when using strings in a cross-platform application, you are in for quite the wild ride. Even something really simple as “I want to list all files in a directory and send the result to a client in JSON format” is just, well, a nightmare, if you are committed to handling all the edge-cases.
- jeroenhd 5y agoThe unfortunate story of Windows is that it tried to do UTF before UTF8 was invented. Java has the same issue. UTF16 existed before UTF8 and now UTF8 has become the norm. That's why you have to deal with wchar_t and other such nonsense in low level languages. As for C++, the standard library has create_directory (https://en.cppreference.com/w/cpp/filesystem/create_directory https://en.cppreference.com/w/cpp/filesystem/create_director...) in the std::filesystem namespace, taking a std::filesystem::path. C++17 may not be as widespread as its older companions, but if you're using the language in a modern context there's a perfectly usable set of standard APIs for this stuff that works with a whole bunch of char types (or the std::u8string if you're on modern C++ and want to forego the whole UTF issue as much as possible). The problem is that you have to deal with edge cases. File system corruption happens and iterating over JSON files in a directory can show random character sequences at any point in time. You can choose to ignore it, or fail on error, or let the language decide what to do in the case of Go, but you always need to be mindful of the failures that can happen. Ignoring the problem won't make it go away and eventually it'll bite you in the ass of you keep doing it.
- OvermindDL1 5y agoRust may give a subset of file system functionality that all general file systems should in theory support in the front interface, but you can access platform specific functionality by asking for the extended interface, which you put inside a platform check block. You have the full functionality there.
- account42 5y ago> The unfortunate story of Windows is that it tried to do UTF before UTF8 was invented. Java has the same issue. UTF16 existed before UTF8 and now UTF8 has become the norm. That's why you have to deal with wchar_t and other such nonsense in low level languages. UTF-8 was first implemented in 1992 while UTF-16 was specified in 1996. Windows and Java use UTF-8 because they retconned their UCS-2 APIs to expect UTF-16 rather than add UTF-8 support. For Windows this is especially damning as they already had a API for encodings with 8-bit code units which they didn't bother adding UTF-8 support to until very recently.
- 5y ago