Is there a way to create a preprocessor macro for

2019-07-15 19:36发布

问题:

Is it possible to create a C++ preprocessor macro based on a function result?

For example, I'd like to save the screen height dynamically in a preprocessor macro definition:

#define SCREEN_HEIGHT   GetSystemMetrics(SM_CYVIRTUALSCREEN)

Then I want to use the result to set values based on the screen height:

#if SCREEN_HEIGHT < 1200
    #define TOP_COORD     200
    #define BOTTOM_COORD  500
    #define LEFT_COORD    0
    #define RIGHT_COORD   1280
#else
    #define TOP_COORD     1100
    #define BOTTOM_COORD  1400
    #define LEFT_COORD    0
    #define RIGHT_COORD   1280
#endif

This doesn't work, as SCREEN_HEIGHT doesn't seem to be getting defined properly.

Is there a better way to accomplish this? Is this even possible? I want to be able to get this screen height info in the header file if possible, as this is part of a large chunk of legacy code.

回答1:

No. Macros are fully evaluated at compile time, conceptually at least by a pre-processor, before the actual code is even compiled, and the expressions in #if, etc. must be preprocessor expressions. The height of the screen cannot be known until the program runs.



回答2:

No, it can't be done. How do you expect to make a compilation-time desition, which depends on a property of the screen in which the application will be run? You can't know that on compilation time.



回答3:

Real functions are usually getting called only runtime. Even function-like things such as sizeof (and generalized constant expressions - thanks to @Pubby for noting) are evaluated by the compiler after the preprocessing stage. By this time all preprocessor stuff has been replaced in the code - the compiler has no idea about things like #if. Therefore what you want is not possible using macros.

With templates, however, it is possible. On second thought, not in this case, because the actual screen height can not be known until runtime.

You could define a struct to contain the coordinate values as constants, and store all of these in a map or other form of collection, then look it up dynamically at runtime, when you know the actual screen height.



回答4:

If GetSystemMetrics would be a macro, then you could do this. If GetSystemMetrics would be constexpr, then you could use traits.

But since GetSystemMetrics is a normal function, you have to work with normal C++.

struct system_metrics_ {
  int top, bottom, left, right;

  system_metrics_()
  {
    if (GetSystemMetrics(SM_CYVIRTUALSCREEN) < 1200) { /* first case */ }
    else { /* second case */ }
  }
};

// define this method outside the header
const system_metrics_& system_metrics() { static system_metrics_ sm; return sm; }

// legacy code
#define TOP_COORD     (system_metrics().top)
#define BOTTOM_COORD  (system_metrics().bottom)
#define LEFT_COORD    (system_metrics().left)
#define RIGHT_COORD   (system_metrics().right)