Можно ли переопределить макрос без использования undef?

Я пытаюсь разобраться в исходном коде libspatialindex. Будучи новичком в С++, я с трудом понимаю концепцию макросов. Оболочка API библиотеки для C, sidx_api.cc, прямо или косвенно включает многочисленные заголовки два из которых, по-видимому, определяют один и тот же макрос, связанный с взаимодействием с динамической библиотекой, без «undef»:

Tools.h

45 #if (defined _WIN32 || defined _WIN64 || defined WIN32 || defined WIN64) && !defined __GNUC__
46  #ifdef SIDX_DLL_EXPORT
47  #define SIDX_DLL __declspec(dllexport)
48  #else
49  #define SIDX_DLL __declspec(dllimport)
50  #endif
51 
52  // Nuke this annoying warning. See http://www.unknownroad.com/rtfm/VisualStudio/warningC4251.html
53 #pragma warning( disable: 4251 )
54 
55 #else
56  #define SIDX_DLL
57 #endif

sidx_export.h

29 #pragma once
30 
31 #ifndef SIDX_C_DLL  
32 #if defined(_MSC_VER)
33 # define SIDX_C_DLL __declspec(dllexport)
34 # define SIDX_DLL __declspec(dllexport)
35 #else
36 # if defined(USE_GCC_VISIBILITY_FLAG)
37 # define SIDX_C_DLL __attribute__ ((visibility("default")))
38 # define SIDX_DLL __attribute__ ((visibility("default")))
39 # else
40 # define SIDX_C_DLL
41 # define SIDX_DLL
42 # endif
43 #endif
44 #endif

Я считаю, что переопределение макроса без undef проблематично, например, как обсуждалось здесь и здесь . Я что-то упустил здесь? Спасибо.


person Amis71    schedule 11.06.2019    source источник
comment
Обычно вы получаете предупреждения при переопределении макросов. Ты?   -  person Quentin    schedule 11.06.2019
comment
Макросы, которые вы показываете, определены только один раз из-за условий.   -  person Some programmer dude    schedule 11.06.2019
comment
@ Какой-то программист, чувак, ты знаешь, что sidx_dll_export в строке 46 всегда будет определен?   -  person Amis71    schedule 11.06.2019
comment
Подумайте о том, чтобы отправить электронное письмо человеку, который поместил свой адрес электронной почты вверху файла? Он строит? Это работает? Различные if defined вокруг них могут остановить переопределение. На высоком уровне dllexport предназначен для экспорта функций для использования вне dll/так, что происходит по-разному в * nix и Windows. Как вы его строите? Для какой платформы?   -  person doctorlove    schedule 11.06.2019
comment
Если я вставлю это в MSVC, то SIDX_DLL действительно будет определено дважды, один раз как __declspec(dllimport) и один раз как __declspec(dllexport). Ой!   -  person Blaze    schedule 11.06.2019
comment
Чего здесь не хватает, так это того, какие макросы определены в настройках проекта при создании исходных файлов, включающих заголовочные файлы.   -  person Some programmer dude    schedule 11.06.2019
comment
@doctorlove В этом проблема, я в основном работаю с python и мне не нужно беспокоиться о макросах. Чтобы ускорить запрос rtree в python, я решил обернуть libspatialindex с помощью ctypes. Прошла всего неделя с тех пор, как я начал читать о синтаксисе C++. Я не запускал этот код и просто хотел понять его различные части.   -  person Amis71    schedule 11.06.2019
comment
Сначала проверьте его сборку. Тогда, возможно, попробуйте написать dll/так самостоятельно, чтобы понять кое-что из этого. Опять же, на высоком уровне некоторые из них предназначены для * nix, а другие - для Windows.   -  person doctorlove    schedule 11.06.2019
comment
@doctorlove Что ж, думаю, это хороший вариант, которым я рано или поздно воспользуюсь.   -  person Amis71    schedule 11.06.2019
comment
@Someprogrammerdude Как я могу узнать о макросах, которые создаются во время компиляции и сборки?   -  person Amis71    schedule 11.06.2019
comment
Проверьте make-файлы и файлы проектов Visual Studio (в зависимости от того, что применимо).   -  person Some programmer dude    schedule 11.06.2019
comment
@Blaze Я не понимаю, как это может произойти. Выполняется только одна из этих #define строк, в зависимости от #ifdef DIX_DLL_EXPORT.   -  person Barmar    schedule 11.06.2019
comment
@Barmar это происходит, если один из этих макросов WIN определен, __GNUC__ не определен, SIDX_DLL_EXPORT не определен и SIDX_C_DLL также не определен.   -  person Blaze    schedule 11.06.2019
comment
Я вижу, он определяется в Tools.h, а затем переопределяется в sidx_export.h.   -  person Barmar    schedule 11.06.2019
comment
@Blaze, вы не должны просто вставлять его и надеяться, что это сработает. Вам нужно полное окружение проекта с его настройками и флагами компиляции.   -  person n. 1.8e9-where's-my-share m.    schedule 11.06.2019


Ответы (2)


Никакой макрос здесь не может быть переопределен незаконно. Некоторые из них могут быть определены более одного раза с идентичными определениями, что нормально. Проблема заключается в неидентичных определениях.

Сама DLL всегда создается с SIDX_DLL_EXPORT, определенным настройками проекта. Код, использующий библиотеку DLL, создается без определения этого макроса.

Второй заголовок является частью DLL. Он всегда компилируется с определением SIDX_DLL_EXPORT. Таким образом, определенные им макросы, если таковые имеются, всегда идентичны макросам, определенным в первом заголовке. Такое идентичное переопределение не составляет проблемы.

person n. 1.8e9-where's-my-share m.    schedule 11.06.2019

Если вы переопределяете его, вам нужно переопределить его, используя точно такой же список токенов предварительной обработки. Это верно как для макросов функций, так и для макросов объектов.

Из 6.10.3 Macroreplacement p2:

Идентификатор, определенный в настоящее время как объектно-подобный макрос, не должен переопределяться другой директивой предварительной обработки #define, за исключением случаев, когда второе определение является объектно-подобным макроопределением и два списка замены идентичны.

Аналогично, идентификатор, определенный в настоящее время как макрос, подобный функции, не должен переопределяться другой директивой предварительной обработки #define, если второе определение не является определением макроса, подобного функции, с тем же количеством и написанием параметров, и два списка замены идентичны.

Итак, в этих определениях используется shall.

Теперь от 4.Conformance p2:

Если нарушается требование «должен» или «не должен», которое появляется за пределами ограничения, поведение не определено.

Это официальное определение. Таким образом, не удаляя символ макроса из среды препроцессора (используя #undef) перед его переопределением, вы получаете неопределенное поведение.

Если вы обнаружите другую ситуацию в некоторых реализациях C, это не стандартизировано.

Правильно было бы вставлять разные определения в зависимости от какого-то условия, которое вы задаете снаружи:

#if COND
#define M M1
#else
#define M M2
#endif

Если вы используете какую-то библиотеку, которая переопределяет его, наверняка есть несколько макросов, которые устанавливают COND и делают вещи хорошо определенными. Вам нужно научиться пользоваться библиотекой. Переопределение с другим списком токенов pp недопустимо.

person alinsoar    schedule 11.06.2019
comment
Приведенная ссылка относится к проекту C99, который устарел. Однако те же самые положения выражены и в двух последующих версиях стандарта. - person John Bollinger; 11.06.2019