How to force the C++ compiler to warn when a specific std class function is called?

Viewed 351

I'm trying to use UTF-8 std::string and std::filesystem::path in a fool-proof way on Windows 10+ where Beta: Use Unicode UTF-8 for worldwide language support is likely off. C++17 or above.

To go from a path to a UTF-8 string, I have to use path.u8string() instead of path.string().

And to go from a UTF-8 string to a path, I have to use u8path() instead of path().

How can I make the compiler help catch me when I call the "wrong" functions? This includes implicit construction of path, that is:

#include <filesystem>
#include <string>

void foo(const std::filesystem::path& path) {}

std::string pathString; // UTF-8 string.
foo(pathString) // Wrong. path() expects native encoding.
foo(u8path(pathString)) // Make sure to always use u8path().

std::filesystem::path path;
path.string() // Wrong. Returns native encoding.
path.u8string() // Make sure to always use u8string().

Can I force the C++ compiler to warn me when specific functions are called? On either MSVC, GCC or Clang, preferably on all? Can I easily prevent myself from making these errors in some other way?

Update Jan 25, 2021

I've been able to fix my underlying issue by calling setlocale(LC_ALL, ".UTF-8"); before any calls to std::filesystem::path() and std::filesystem::path::string(). This seems to make the MSVC standard library accept and give out strings as UTF-8 on Windows 7+ with Visual C++ 2019 (relevant Microsoft STL issue).

After that I found Use UTF-8 code pages in Windows apps (can't believe it took me so long to find) which also makes std::filesystem::path work in UTF-8, but additionally also makes argv, envp and all A- Win32 API calls use UTF-8 (requires Windows 10, Version 1903).

But the initial question is still valid--how to warn or prevent the use (poison) of arbirary functions.

2 Answers

GCC and clang have a poison pragma which you might find helpful here. Unfortunately, you can only poison tokens, and not fully qualified names, so while you can do:

#pragma GCC poison string

you cannot do (for example):

#pragma GCC poison std::filesystem::path::string

Still, maybe this is something you could wrap in a #ifdef and enable it periodically in a test build to weed out errors in your code.

Here's a fully worked example:

#include <filesystem>
#include <string>

#pragma GCC poison string

int main ()
{
    std::filesystem::path path;
    auto x = path.string();         // error: attempt to use poisoned "string"
    auto y = path.u8string();       // OK
}

Live demo

Also, see this question for an MSVC solution. More #ifdef's coming to a screen near you :)

A hacky approach I've used in similar situations in the past: Edit the system header(s) and add [[deprecated]] to the functions that you don't want to use.

Related