-1

I have been playing with writing Unicode code using Windows API and have found one thing particularly frustrating.

I made a simple interface for wrapping "MessageBox" into an "alert", reducing number of arguments needed to call it from 4 to 1.

The issue is that this one argument MUST be called with L"My string", which I want to avoid.

Here is my interface so far:

UseCase.hpp

#pragma once
#include <string>
#include <iostream>

/**
 * The User should not need to know any Windows API ideally.
 * They will have to know L"Str" notation though...
 */
namespace UseCase
{
    void alert(const wchar_t *message);
};

UseCase.cpp

#include <Windows.h>
#include "UseCase.hpp"

/**
 * Kind-of like javascript:alert(message).
 * This is a very common usecase, and heavily simplifies call convention.
 */
void UseCase::alert(const wchar_t *message)
{
    MessageBox(0, message, L"message box", MB_OK | MB_ICONEXCLAMATION);
}

main.cpp

#include "UseCase.hpp"

using namespace UseCase;

const wchar_t *msg = L"Привет мир";

int wmain(int argc, wchar_t **argv)
{
    alert(msg);

    return 0;
}

My concern is that no matter how the main tries to call alert, it has to use L"String" notation which is visually annoying to me.

To elaborate why L is annoying to me, it is for two reasons:

  1. It treats Rvalue string literals differently from variables containing an ascii string.
  2. If you try to call it on an ascii string "hello, world", it will give a confusing error message to an average user.
  3. It's obvious that the string cannot be stored in ascii, and you are trying to assign it to a unicode string, there shouldn't be much getting in a way of an automatic conversion.

Is there a way to get rid of the L"String" convention and make the program automatically treat the function input as wide character input?

Current viable solutions are:

  1. Create a macro for calling messagebox with a string literal that wraps it in L for you. The issue with this approach is the cryptic error messages on variables passed into the function, as L cannot convert variables from ascii to unicode for you.
  2. Others proposed but I haven't fully wrapped my head around implementing them.
  3. Have the user register their string externally and stored as unicode inside a file/binary cache. I can then provide an interface to load this string as a wide string and call the function using it.
Dmytro
  • 5,068
  • 4
  • 39
  • 50
  • Please provide a link to that language "C/C++". Until then, your code is C++. – too honest for this site Feb 29 '16 at 21:15
  • Sorry. I thought it would be acceptable since I only used namespaces/hpp from c++. I'll keep it in mind for future questions. – Dmytro Feb 29 '16 at 21:17
  • 3
    `L` is for _wide string literal_. Not necessarily Unicode. – chux - Reinstate Monica Feb 29 '16 at 21:18
  • 2
    ""oh, you clearly meant unicode, let me fix this for you", and convert it to unicode automagically." Sure, use Elf file format and get a wizzard. Too bad Gandalf is not available. – too honest for this site Feb 29 '16 at 21:18
  • Sorry I thought it would make the post more interesting to read. Let me remove the witty remarks. I'll keep this in mind in my future posts. Thanks. Why am I being downvoted without any explanation on how to improve my question? – Dmytro Feb 29 '16 at 21:19
  • If you want to use wide character string literals without and `L` prefix, you have to pick another programming language. Chances are, that other programming languages also offer rules that you might perceive as *"visually annoying"*. You might consider getting professional about this. Those are the rules. – IInspectable Feb 29 '16 at 21:23
  • I am okay with there not being a way to do this. I merely want to be certain that there is no such way. If this is indeed the standard way to deal with wide strings I will just accept it. – Dmytro Feb 29 '16 at 21:24
  • 2
    Why the letter L is visually annoying is interesting? Did you have a bad time watching sesame street? – Ed Heal Feb 29 '16 at 21:24
  • @Ed Heal I want to minimize un-necessary code in the main. If I give this code to a 12 year old and he tries to pass "Hello, World" to it, it won't compile, and I will get confused looks when I add the L before it and it works. It is also strange because if they pass a regular char * into it, L won't even work anymore, and I will need to do really punitive conversion, which needs extra care not to leak memory. I was curious if there were ways to avoid this. – Dmytro Feb 29 '16 at 21:27
  • Unrelated to your immediate question: You dropped an **important** argument in your 4-to-1-wizardry. If you create a message box unowned, it will not display in front of its owner. It may just disappear behind the designated owner, with a taskbar button being the only hint that it's there. – IInspectable Feb 29 '16 at 21:27
  • @IInspectable yes, the code is not refined, it's there for demonstration of my issue, it's nowhere near production quality, you're right. – Dmytro Feb 29 '16 at 21:28
  • 1
    If you want to teach programming to a 12 year old, C++ might not be the finest choice. Pick [Logo](https://en.wikipedia.org/wiki/Logo_(programming_language)) instead, for example, one of the few educational programming languages. – IInspectable Feb 29 '16 at 21:30
  • @IInspectable that's not my point. My question was whether such an interface exists or not or if it can be created. I am well aware C++ is not the only language that exists. I can accept it as the standard way to deal with unicode and move on if it's not possible. – Dmytro Feb 29 '16 at 21:30
  • If you are teaching adults to program what is the problem with the letter L at the start of a string? – Ed Heal Feb 29 '16 at 21:42
  • @EdHeal because it doesn't work when you pass a char* variable to it, converting from char* to wchar_t * if you never done so before is error prone and confusing and is not the intent of your program. If you are used to it, it works fine, but your code is far too complicated for what it actually wants to do, pass a string to a function that only wants to accept wide strings to begin with. It's hard enough to force you to stop what you're doing and focus on converting the string, instead of just calling the function. – Dmytro Feb 29 '16 at 22:02
  • @Dmitry There's nothing stopping you providing an overloaded function that accepts a `char*` string. – Jonathan Potter Feb 29 '16 at 22:40
  • @JonathanPotter You're right, and I can also overload it to behave differently on Rvalue and Lvalue string inputs. This solution seems significantly better than the earlier ones, and appears viable, but requires a lot of context sensitive code for every single function that I want to behave in this way, which adds a lot of complexity. – Dmytro Feb 29 '16 at 22:54
  • You could write a wrapper that takes `char32_t *` so you could then pass U"Hello World". It makes a lot more sense than L. – Tom Blodget Mar 01 '16 at 02:31

2 Answers2

4

If you want to be able to pass Unicode strings (i.e. use MessageBoxW, which takes wide-character strings), but you want to be able to use plain string literals without the L prefix, you'll need to decide on an encoding to use for the Unicode characters in a narrow string, and then perform a run-time conversion to a wide string according to the encoding.

UTF-8 might be a reasonable start. See this answer for how to convert from/to UTF-8 using standard Windows API.

Community
  • 1
  • 1
Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
2

You can get around this by making the actual call to alert a preprocessor macro, and using the _TEXT() macro provided by MS (or figure out how to do token pasting L ## msg yourself correctly.)

#define DMITRY_ALERT(msg)  UseCase::alert(_TEXT(msg))

this will "magically" insert the L in any UNICODE build at the cost of having to use a macro wrapper (that needs to have an UGLY_LONG_NAME to prevent clashes).

Note: That macro can then only be called with string literals, not with a variable.

Whether that is worth the trouble seems doubtful to me.

Martin Ba
  • 37,187
  • 33
  • 183
  • 337
  • 1
    This is a real solution, I was hoping for one without using a macro to accomplish it though. – Dmytro Feb 29 '16 at 21:34
  • 2
    `DMITRY_ALERT(my_funny_string_variable);` - Have fun with the undecipherable error messages that ensue. – IInspectable Feb 29 '16 at 21:36
  • 1
    That's a big part why I don't want to use this approach, along with L's unintuitive difference in how it treats a variable containing "hello" and an Rvalue "hello" literal. – Dmytro Feb 29 '16 at 21:37