9

I can't figure out what this macro means:

#define DECLARE_HANDLE(n) typedef struct n##__{int i;}*n

DECLARE_HANDLE(HWND);

I have learned from the C program that:

"##" means connect the parameter.

so the macro equals:

typedef struct HWND__{int i;}*HWND

Is this right?

If it is right, what is the meaning of that sentence?

==================

Code from a game Bombermaaan (for Windows and Linux),
link http://sourceforge.net/p/bombermaaan/code/HEAD/tree/trunk/src/Bombermaaan/winreplace.h,
line No 90.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
puwei219
  • 547
  • 1
  • 4
  • 8
  • what does this mean? #define INVALID_SOCKET (SOCKET)(~0) – riqitang May 21 '13 at 11:56
  • 1
    `~` is the *logical not* operator, which in this case will revert all the bits of the number in which you put 0 in. `char sock = INVALID_SOCKET;` will be 0xFF, `int sock = INVALID_SOCKET` will be 0xFFFFFFFF. – Gui13 May 21 '13 at 12:01
  • thanks @Sean for ask the question, a good question. and thanks Xgbi for your very detail answer. – puwei219 May 21 '13 at 12:13
  • 1
    It means, in part, that somebody likes to create invalid identifiers. Names that contain two consecutive underscores are reserved to the implementation and should not be used in any other code. – Pete Becker May 21 '13 at 15:04
  • @xgbi: `~` will *invert*, not revert, the bits. – Keith Thompson May 21 '13 at 15:11
  • @PeteBecker, The `HWND__` part I can understand. The Windows API is pretty much meant to be used with MSVC (and yes, I use it with GCC daily), and Microsoft *is* an implementer. I like to think of it as implementation code. – chris May 21 '13 at 22:16
  • 1
    @xgbi, I would argue that `!` is the logical not operator. `~` is better off the bitwise not operator. – chris May 21 '13 at 22:17
  • @chris - those names are reserved to the **implementation**, not to the **implementor**. And the implementation is the compiler and standard library; the language definition does not extend this allowance to whatever else the implementor happens to want to throw into the box. Playing fast and loose with reserved identifiers makes it harder for other implementations to work with such non-conforming headers. Unfortunately, most OS vendors think they are part of the implementation... – Pete Becker May 21 '13 at 23:23
  • @PeteBecker, Yeah, it's a real shame, I know, but I doubt it's going to change any time soon. There are actual libraries that use reserved identifiers as well, which also sucks. – chris May 22 '13 at 00:04
  • @KeithThompson : yeah well, I'm not english, excuse the wording :) – Gui13 May 22 '13 at 06:56

5 Answers5

11

The main purpose of this construct is to prevent the misuse of handles. If all handles are simply void * or int or long long or some other basic type, there is nothing to prevent you from using one instead of another. A pointer to a struct HWND__ and pointer to struct HBITMAP__ isn't the same thing, so if you have a the following code:

HWND hwnd;
HBITMAP hbmp;

hbmp = GetBitmap(...);
hwnd = hbmp;    // gives compiler error. 

It's a fairly classic technique to ensure that you get unique types for something that the API supplier don't want to provide the true declaration for. Although I'm not entirely sure why they even need a proper struct declaration, you could probably get away with:

#define DECLARE_HANDLE(n) struct n ## __; struct n ## __ *n;

That will also ensure that any dereferece HWND won't be possible, since the compiler will object to "use of incomplete type".

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • It's a *little* bit annoying if you want to use a smart pointer to clean it up, though, and I'm not sure if holding a `decltype(*std::declval()>)` is safe (I was going to ask about that sometime). Doing that makes for a nice RAII `Handle` class. Keep in mind `STRICT` plays a part here as well. – chris May 21 '13 at 14:40
  • @Chris: Probably a better idea to wrap the whole HWND handling into a class that creates the window object, handles it, and then on destruction closes it. – Mats Petersson May 21 '13 at 22:05
  • Well, windows are a little finicky (you don't want to acquire someone else's window and have it do that for you), `HWND` is just the first handle that comes to my mind. My plan with this was to make a pure `Handle` class for holding the handle and cleaning up with the user-supplied function and to have actual classes that represent these objects and can use the `Handle` class to manage the resource more easily. In an ideal world, the user wouldn't have to know the `Handle` class exists. – chris May 21 '13 at 22:11
  • @chris: I guess you could do something like that with templates, and just write a "closing function" for the types that you know what to close them with, and let the user supply one if there are multiple choices. – Mats Petersson May 21 '13 at 22:20
  • Definitely, I've already done that to test it out. The smart pointer uses that trick for what to point to and `Handle` would be one example of use, with lambdas being amazing for specifying cleanup code (I guess an alteration for `T(WINAPI *)(HandleType)` could be useful if no error checking is done). I [asked](http://stackoverflow.com/questions/16676701/is-relying-on-the-type-of-a-windows-handle-being-a-pointer-ok) about the whole assuming it's a pointer thing, though. – chris May 21 '13 at 22:22
6

Your assumption is correct.

You can check this with the following simple test code.

DECLARE_HANDLE(HWND);
struct HWND__ s;
HWND p = (HWND) malloc(sizeof(struct HWND__));
s.i = 20;
p->i = 100;
cout << "i valueis " << s.i<<" and " << p->i <<endl;
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Whoami
  • 13,930
  • 19
  • 84
  • 140
2

Exactly.

This is used to declare an opaque pointer to a structure that is unknown to you.

I don't know why they didn't simply declare it as a

typedef void* HWND;

Probably for alignment issues, since structures can be aligned but not basic types.
As mentioned above, declaring the type as a structure permits some compile-time type checking.
Clever!

Gui13
  • 12,993
  • 17
  • 57
  • 104
  • It is mainly so that you use HWND where you want HWND, and HBITMAP where you want HBITMAP, etc. If they are just `void *`, you can do `HWND h = CreateBitmap(...);`. – Mats Petersson May 21 '13 at 12:19
  • As Mats mentioned, void* doesn't allow typechecking (diffrent handle types can be mixed). However, I don't understand why not simply #define DECLARE_HANDLE(n) typedef struct n##__ *n; (without actual struct definition) that would provide true opacity and prevent from something like ++myhandle. – user396672 May 21 '13 at 12:37
  • in non-STRICT builds, I believe it is `typedef`ed to void *. See https://msdn.microsoft.com/en-us/library/windows/desktop/aa383681%28v=vs.85%29.aspx – Rob K Aug 21 '15 at 20:54
1

DECLARE_HANDLE(HWND); indeed expands to

typedef struct HWND__{int i;}*HWND;

Now, you're asking what it means? Just split the typedef into two steps:

struct HWND__
{
    int i;
};

typedef HWND__* HWND;  // C++ only; for both C++ and C: typedef struct HWND__* HWND;

Thus, it defines a struct HWND__ containing an int (named i) and declares a type alias HWND as pointer to HWND__.

gx_
  • 4,690
  • 24
  • 31
0

It defines a HWND as a pointer to struct HWND__ that contains int i.

So, now you can use in the code the type HWND.

Alex
  • 9,891
  • 11
  • 53
  • 87