简体   繁体   中英

Changing type of variable using prepocessor's #ifdef

I have two applications 'server' and 'client' which rely on a lot of the same code. They need to behave differently in the way they interact with the console. Instead of writing one class which handles both cases, I wanted to include two different classes in the two different cases. Now, in the Globals.h and Globals.cpp I do this:

Globals.h:

#ifdef SERVER
extern ConsoleUI ui;
#else
extern Logger ui;
#endif

Globals.cpp:

#include "Globals.h"
#ifdef SERVER
ConsoleUI ui;
#else
Logger ui;
#endif

(I also have include guards in the header, but left them away here).

In the Server's main file, I define:

#define SERVER

and in the client's main file, I use:

#define CLIENT

However, both of them end up creating a Logger ui instead of a ConsoleUI ui. I know this because the server tries to call a function which exists in ConsoleUI and segfaults, because for some reason his "ui" is of type "Logger".

I do realize that a cleaner way might be to seperate the Globals into GlobalsClient.h/.cpp and GlobalsServer.h/.cpp, but I want to understand why the above code doesn't work.

Hope this hasn't been asked before, everything I searched for didn't give me any useful hits.

The reason why the code you have is likely failing is because if you do something like #define SERVER that will create the SERVER symbol. If you also had #define CLIENT somewhere else too then the following:

#include "Globals.h"
#ifdef SERVER
ConsoleUI ui;
#else
Logger ui;
#endif

Would actually go down the ifdef SERVER branch which might not be what you want.

In order to do the conditional compilation in the way that you want you need to compile once for the Server and once for the Client using a #define that is from the command line. The flag to do this is usually -D .

So using the #ifdef approach to compile for the server you would do something like

g++ -DSERVER -o server_output_name

and for the client:

g++ -DCLIENT -o client_output_name

However it's important to realize that generally speaking there's better ways of achieving what you want in c++. Specifically you want to get the language to do the type checking for you, not relying the relatively speaking "unsafe" preprocessor. If there is a lot of common code what about making a base class that contains that code and having 2 derived classes that implement the non-common code?

something like:

class UIBase{

protected:
 //all the common code
};


class UIServer : public UIBase{
//server specific code and implementation
};

class UIClient : public UIBase{
//client specific code and implementation
};

This way you can create the objects based on their types and have the c++ language make sure that the right code is called based on the context. You get some type safety this way that you don't get with the #define approach.

The problem is probably that you haven't consistently defined SERVER or CLIENT properly everyone. Change Globals.h to:

#ifdef SERVER
#ifdef CLIENT
#error "Both CLIENT and SERVER defined!"
#endif
#define ui ServerUI
extern ConsoleUI ui;
#elif defined(CLIENT)
#define ui ClientUI
extern Logger ui;
#else
#error "Neither CLIENT nor SERVER defined!"
#endif

Now if you have missing or other broken settings of SERVER and/or CLIENT, you'll get a compiler or linker error, instead of a runtime crash.

确保在主cpp中包含“ Globals.h”之前放入“ #define SERVER”。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM