-2

So imagine I had the code:

#define ID_BUTTON 1

in order to give myself a variable to use when using the CreateWindow() function to create a button.

Now, is it possible for me to use a method to return ID_BUTTON? Usually I'd find it relatively easy, but I don't know what function type I'd have to specify.

So,

1) Is it possible?

2) If so, what function type would I have to use?

Remember it'll just be simple code like:

<function_type> getID() {
return ID_BUTTON;
}

Thanks,

Jay

Jay
  • 81
  • 1
  • 7

2 Answers2

1

Technically speaking, what you want is actually possible using C++11 decltype:

#define ID_BUTTON 1

auto getID() -> decltype(ID_BUTTON)
{
    return ID_BUTTON;
}

int main()
{
    auto x = getID();
}

There, no need to specify int anywhere for ID_BUTTON or the function.

The real answer to your question, however, should be: Don't use macros for constants. C++ has const (and, as of recently) constexpr for that.

Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
  • 2
    For `ID_BUTTON`, if he has a resource file he'll have to use a `#define` unless he wants two separate header files, one for C++ and one for the resource file. – andlabs Mar 05 '15 at 17:09
  • @andlabs: Yes, good point. My advice was on a strict C++ language level. – Christian Hackl Mar 05 '15 at 17:13
  • It's still valuable information; I've pointed to this post in my answer (which covers different things such as control identifiers). – andlabs Mar 05 '15 at 17:29
0

Assuming you don't understand what

#define ID_BUTTON 1

even does:

The #define, const, constexpr, and enum statements don't create variables; they create cconstants. Both variables and constants are identifiers that refer to values in your program. The difference is that constants cannot be changed at runtime, while variables can.

#define doesn't create just any ordinary constant, however. It actually tells a part of the compiler called the preprocessor to actually replace the text ID_BUTTON in your source code with the string 1. This can be useful, and this is what you have to do for C, but in C++, a constexpr (which was introduced in C++11) is usually more optimal. See Christian Hackl's answer for more on that.

If you're going to use a constant to pass values to an API, which is what you're planning on doing with ID_BUTTON, the API will usually tell you what type it expects, so you can use that type instead of having the compiler determine the best type for the job. However, see my discussion below on control identifiers in Windows, as there's a catch for the specific case of ID_BUTTON.

If you're in control of the type, you'll have to use a little more thought to figure out what type to use. In some cases, the language's type promotion rules will let you get away with just saying 1 without a type, but if you're creating known small or large integers, or bit fields, or floating-point numbers, you'll need to use the appropriate type name or qualifiers (short, long, unsigned, float, double, etc.). It takes a little time to figure out where to go, but once you've done it enough you'll be natural at it.


Assuming you don't understand how control identifiers in Windows works:

Most of the API functions that take a control identifier, such as GetDlgItem(), take the control identifier as an int; your control identifiers should thus have type int.

However, CreateWindow() and CreateWindowEx() expect the control identifier as the third to last parameter. This parameter is of type HMENU. C++ won't let you shove an int into an HMENU, so you'll have to use a cast: (HMENU) ID_BUTTON. (There's probably an equivalent C++-style cast, but I don't know/forget what it is.)

In addition, if you're going to use a resource file, you'll have to use #define statements to create your ID_xxx constant names, as the resource file format doesn't have const, constexpr, or enum. In this case, continue using #define for the identifiers, shove these #defines in their own include file, and #include that file from both your C++ source and the resource file.

If you're not using a resource file, you don't need to worry about using control identifiers at all; just use the HWND for each control directly. You should still assign control identifiers in case you need tab navigation or other dialog box message processing in your windows, but other than that you can just use the HWNDs directly. (I forget if control identifiers are involved in tab navigation or not.)

Finally, there are a number of predefined control identifiers that have special meaning to the dialog manager. This is from Microsoft's winuser.h:

/*
 * Dialog Box Command IDs
 */
#define IDOK                1
#define IDCANCEL            2
#define IDABORT             3
#define IDRETRY             4
#define IDIGNORE            5
#define IDYES               6
#define IDNO                7
#if(WINVER >= 0x0400)
#define IDCLOSE         8
#define IDHELP          9
#endif /* WINVER >= 0x0400 */

Your own control identifiers should avoid colliding with these; your ID_BUTTON conflicts with IDOK. (If they do collide, you'll see weird things like your controls activating on keyboard shortcuts that the dialog manager knows about, for example.) The canonical solution to this problem is to start numbering your control identifiers at 100 (I believe Visual Studio was responsible for this, but I don't know for sure).

andlabs
  • 11,290
  • 1
  • 31
  • 52
  • `#define` doesn't create a constant. When I write `foo(ID_BUTTON);` there is no constant. Just a pre-processor macro expansion and an `int` literal. – David Heffernan Mar 05 '15 at 18:04
  • Right; my point was to distinguish between a variable and a constant in a simple way. I do talk about preprocessor replacement in the next paragraph. – andlabs Mar 05 '15 at 18:26