Изменение типа переменной с использованием prepocessor #ifdef

У меня есть два приложения "сервер" и "клиент", которые полагаются на много одного и того же кода. Им нужно вести себя по-разному в том, как они взаимодействуют с консолью. Вместо того, чтобы писать один класс, который обрабатывает оба случая, я хотел включить два разных класса в два разных случая. Теперь, в Globals.h и Globals.cpp, я делаю это:

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

(У меня также есть охранники в заголовке, но они оставили их здесь).

В главном файле сервера я определяю:

#define SERVER

и в основном файле клиента я использую:

#define CLIENT

Тем не менее, оба из них в конечном итоге создают Logger ui вместо ConsoleUI ui. Я знаю это, потому что сервер пытается вызвать функцию, которая существует в ConsoleUI и segfaults, потому что по какой-то причине его "ui" имеет тип "Logger".

Я понимаю, что более чистым способом может быть разделение Globals на GlobalsClient.h/.cpp и GlobalsServer.h/.cpp, но я хочу понять, почему приведенный выше код не работает.

Надеюсь, что этого не задавали раньше, все, что я искал, не приносило мне никаких полезных хитов.

3 ответа

Причина, по которой ваш код, вероятно, не работает, заключается в том, что если вы сделаете что-то вроде #define SERVER, который создаст символ SERVER. Если у вас также есть #define CLIENT другом месте, то следующее:

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

Фактически спустился бы на ifdef SERVER которая может быть не такой, какой вы хотите.

Чтобы выполнить условную компиляцию так, как вам нужно, вам нужно скомпилировать один раз для Сервера и один раз для Клиента, используя #define который из командной строки. Флаг для этого обычно -D.

Поэтому, используя подход #ifdef для компиляции для сервера, вы должны сделать что-то вроде

g++ -DSERVER -o server_output_name

и для клиента:

g++ -DCLIENT -o client_output_name

Однако важно понять, что, вообще говоря, есть лучшие способы достижения того, чего вы хотите в c++. В частности, вы хотите, чтобы язык выполнял проверку типов для вас, не полагаясь на относительно говорящий "небезопасный" препроцессор. Если существует много общего кода, что делать с базовым классом, который содержит этот код, и имеет 2 производных класса, реализующих не общий код?

что-то вроде:

class ******{

protected:
 //all the common code
};


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

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

Таким образом, вы можете создавать объекты на основе их типов и иметь язык c++, чтобы убедиться, что правильный код вызывается на основе контекста. Вы получаете некоторую безопасность типов таким образом, что вы не получаете подход #define.


Вероятно, проблема заключается в том, что вы не всегда правильно определяли SERVER или CLIENT. Изменить Globals.h:

#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

Теперь, если у вас отсутствуют или другие сломанные настройки SERVER и/или CLIENT, вы получите ошибку компилятора или компоновщика вместо сбоя во время выполнения.


Убедитесь, что вы поставили '#define SERVER', прежде чем включать 'Globals.h' в свой основной cpp.

licensed under cc by-sa 3.0 with attribution.