Best way to handle large number of preprocessor definitions?



  • Hello,

    I used to use a precompiled header for that. Is there another method?
    Putting "DEFINES" in the project file seems problematic for various reasons.

    What is the "right" way to do it?


  • Moderators

    What do you need to do with those definitions?

    You can try const variables. These are like "#define"s, but you also get compiler type-checking.



  • Thank you for your reply!

    Actually that's where I decide what part of code are being compiled.
    One thing I forgot to mention: the application is a Qt application, but can also be compiled with MSVS for compatibility reasons. So I need to support Qt and MSVS compilation schemes.
    Typically, I have things similar to that (following is only a small extract):

    [code]
    #ifdef RELEASE_EVALUATION
    #define CHECK_LICENSE
    #define CHECK_WEEKLYPING
    #define COMPILATION_VERSION COMPILATION_VERSION_EVALUATION
    #define OUTPUT_FILENAME "myApp.dll"
    #define LIB3D_IMPORTER_REQUIRED
    #define SHOW_EVALUATION_VERSION_TAG
    #define DEFAULT_OPTIONS (ALLOW_IMPORT_EXPORT|ALLOW_SCRIPT_EDITION|ALLOW_EDIT_MODES)
    #endif

    #ifdef RELEASE_FULL
    #define CHECK_LICENSE
    #define CHECK_WEEKLYPING
    #define COMPILATION_VERSION COMPILATION_VERSION_PAYING
    #define OUTPUT_FILENAME "myApp.dll"
    #define DONT_START_IF_NO_VALID_LICENSE_FOUND
    #define LIB3D_IMPORTER_REQUIRED
    #define DEFAULT_OPTIONS (0)
    #endif

    #define ROTATE_LEFT_16(data,amount) (WORD(WORD(WORD(data) << (amount&0x0f)) | (WORD(data) >> (16-(amount&0x0f))))
    #define XXX_MIN(a,b) (((a)<(b)) ? (a) : (b))
    #define XXX_MAX(a,b) (((a)>(b)) ? (a) : (b))
    #define IS_BIT_SET(var,bit) (((var) & (1<<(bit)))!=0)
    #define SET_BIT(var,bit) ((var) |= (1<<(bit)))
    #define CLEAR_BIT(var,bit) ((var) &= (~(1<<(bit))))
    #define TOGGLE_BIT(var,bit) ((var) ^= (1<<(bit)))
    #define SET_CLEAR_BIT(var,bit,on) ((on) ? SET_BIT((var),(bit)) : CLEAR_BIT((var),(bit)) )
    #define RAND_FLOAT (rand()/(float)RAND_MAX)
    #define stdVector std::vector
    #define SERVER_PACKET_SIZE 620
    #define FLOATING_SERVER_PACKET_SIZE 320
    [/code]


  • Moderators

    [quote author="floatingWoods" date="1353157652"]Thank you for your reply!

    Actually that's where I decide what part of code are being compiled.

    ...

    [code]
    #ifdef RELEASE_EVALUATION
    #define CHECK_LICENSE
    #define CHECK_WEEKLYPING
    #define COMPILATION_VERSION COMPILATION_VERSION_EVALUATION
    #define OUTPUT_FILENAME "myApp.dll"
    #define LIB3D_IMPORTER_REQUIRED
    #define SHOW_EVALUATION_VERSION_TAG
    #define DEFAULT_OPTIONS (ALLOW_IMPORT_EXPORT|ALLOW_SCRIPT_EDITION|ALLOW_EDIT_MODES)
    #endif

    #ifdef RELEASE_FULL
    #define CHECK_LICENSE
    #define CHECK_WEEKLYPING
    #define COMPILATION_VERSION COMPILATION_VERSION_PAYING
    #define OUTPUT_FILENAME "myApp.dll"
    #define DONT_START_IF_NO_VALID_LICENSE_FOUND
    #define LIB3D_IMPORTER_REQUIRED
    #define DEFAULT_OPTIONS (0)
    #endif
    [/code]
    [/quote]
    You're welcome :)

    If you want to choose which part of the code to compile, #define and #ifdef is probably the easiest way... but it will make your code messier.

    [quote]One thing I forgot to mention: the application is a Qt application, but can also be compiled with MSVS for compatibility reasons. So I need to support Qt and MSVS compilation schemes.[/quote]Sorry, I'm not sure what you mean here. Do you mean compile using Qt Creator, as well as MSVS? You can use MSVS to compile Qt applications.

    [quote]
    [code]
    #define ROTATE_LEFT_16(data,amount) (WORD(WORD(WORD(data) << (amount&0x0f)) | (WORD(data) >> (16-(amount&0x0f))))
    #define XXX_MIN(a,b) (((a)<(b)) ? (a) : (b))
    #define XXX_MAX(a,b) (((a)>(b)) ? (a) : (b))
    #define IS_BIT_SET(var,bit) (((var) & (1<<(bit)))!=0)
    #define SET_BIT(var,bit) ((var) |= (1<<(bit)))
    #define CLEAR_BIT(var,bit) ((var) &= (~(1<<(bit))))
    #define TOGGLE_BIT(var,bit) ((var) ^= (1<<(bit)))
    #define SET_CLEAR_BIT(var,bit,on) ((on) ? SET_BIT((var),(bit)) : CLEAR_BIT((var),(bit)) )
    #define RAND_FLOAT (rand()/(float)RAND_MAX)
    #define stdVector std::vector
    #define SERVER_PACKET_SIZE 620
    #define FLOATING_SERVER_PACKET_SIZE 320
    [/code]
    [/quote]
    People used to manipulate bits to save memory. But, modern computers have lots of memory, so there is not much benefit to manipulating bits directly any more. I would recommend using normal variables/enums instead of bit-manipulation preprocessor macros. It makes code easier to maintain, and you can remove lots of "#define"s.

    Also, instead of
    @
    #define stdVector std::vector
    @
    it's better to use
    @
    typedef std::vector stdVector;
    @



  • Thanks again JKSH!

    Sorry for the confusion. Actually the original application was a visual studio project and pure MFC. That application can still be compiled within visual studio (i.e. all Qt code is excluded in that case with many ifdef-s).

    When compiling with Qt Creator, then all the MFC code gets excluded (here too, with lots of ifdef-s).

    That's actually the main reason why I have so many "defines" in my precompiled header!



  • If you have a lot of "global" defines, a common approach is to put them all into a file "config.h" (or however you prefer to call it) and include that header file from all other files where you might need those defines.

    For example, you could put the following code into your "config.h" file:

    @ #ifdef RELEASE_EVALUATION
    #define CHECK_LICENSE
    #define CHECK_WEEKLYPING
    #define COMPILATION_VERSION COMPILATION_VERSION_EVALUATION
    #define OUTPUT_FILENAME "myApp.dll"
    #define LIB3D_IMPORTER_REQUIRED
    #define SHOW_EVALUATION_VERSION_TAG
    #define DEFAULT_OPTIONS (ALLOW_IMPORT_EXPORT|ALLOW_SCRIPT_EDITION|ALLOW_EDIT_MODES)
    #endif

    #ifdef RELEASE_FULL
      #define CHECK_LICENSE
      #define CHECK_WEEKLYPING
      #define COMPILATION_VERSION COMPILATION_VERSION_PAYING
      #define OUTPUT_FILENAME "myApp.dll"
      #define DONT_START_IF_NO_VALID_LICENSE_FOUND
      #define LIB3D_IMPORTER_REQUIRED
      #define DEFAULT_OPTIONS (0)
    #endif
    
    [...]@
    

    And then, in your project file, you only add either "-DRELEASE_EVALUATION" or "-DRELEASE_FULL" to the compiler flags, depending on what configuration of the app your are going to build this time.

    BTW: You make it sound like building the app with "Qt" (you probably mean qmake + mingw here?!) and "Visual Studio" (msvc) are two mutually exclusive things. But Qt-based applications can be built with Visual Studio just fine, so you might be able to drop MFC altogether. Making the switch from MFC to Qt is such a big relief! :-)



  • Thanks MuldeR!

    The idea to put all defines into a config file is fine, but what if I forget to include that config file in another file? Then it might just compile file, but with the wrong defines. For example, if my config file has following content:

    [code]
    #define INCLUDE_SPECIAL_FUNCTIONALITY
    [/code]

    and my other file has something like:

    [code]
    #ifdef INCLUDE_SPECIAL_FUNCTIONALITY

    do something

    #else

    do something else

    #endif
    [/code]

    If I forget to include the config file, it will compile fine, with the "do something else" code. But that should rather be the "do something" code.

    That is a bug difficult to catch. With a precompiled header, you know that it is used for all other files.

    Then, I know that I can compile a Qt project with MSVC, and I am also doing it. I just need to keep the MFC version too to support old versions of the application. I will completely drop the MFC code in 1-2 years



  • Well, to prevent that case, you could actually assign a value to those preprocessor definitions:

    @ #ifdef RELEASE_EVALUATION
    #define INCLUDE_SPECIAL_FUNCTIONALITY 1
    #endif

    #ifdef RELEASE_FULL
    #define INCLUDE_SPECIAL_FUNCTIONALITY 0
    #endif@

    And then:

    @#if defined(INCLUDE_SPECIAL_FUNCTIONALITY) && (INCLUDE_SPECIAL_FUNCTIONALITY == 1)

    /* do something */

    #elif defined(INCLUDE_SPECIAL_FUNCTIONALITY) && (INCLUDE_SPECIAL_FUNCTIONALITY == 0)

    /* do something */

    #else

    #error Configuration error. Forgot to include config.h ???

    #endif@

    Also you might want to do something like this in your config.h:

    @#if defined(RELEASE_EVALUATION)

    /* defines for evolution here*/

    #elif defined(RELEASE_FULL)

    /* defines for full version here*/

    #else

    #error Neither RELEASE_EVALUATION nor RELEASE_FULL is defined!

    #endif@

    [EDIT]

    You may also do this, at least in Visual Studio:
    http://msdn.microsoft.com/en-us/library/8c5ztk84(v=vs.100).aspx

    bq. /FI (Name Forced Include File)
    This option has the same effect as specifying the file with double quotation marks in an #include directive on the first line of every source file

    You can do the same with MinGW/GCC by adding "-include config.h" to your CFLAGS/CXXFLAGS, I think.


  • Moderators

    [quote author="floatingWoods" date="1353165219"]Actually the original application was a visual studio project and pure MFC. That application can still be compiled within visual studio (i.e. all Qt code is excluded in that case with many ifdef-s).

    When compiling with Qt Creator, then all the MFC code gets excluded (here too, with lots of ifdef-s).[/quote]

    You don't have to do that. Like MuldeR said, you can use Visual Studio to compile Qt programs.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.