Столкновение между функциями C, импортированными в пространство имен и импортированными в глобальное пространство имен

76
4

В моем коде я привожу заголовки OpenSSL в пространстве имен, например:


#include <cstdio>
namespace OpenSSL {
#include <openssl/ssl.h>
#include <openssl/err.h>
}

Но я только что обнаружил, что это, похоже, заставляет вещи взорваться, если я попытаюсь это сделать, работая с Boost ASIO, которая поддерживает OpenSSL, но, похоже, приносит символы OpenSSL в глобальное пространство имен. Есть ли что-нибудь, что я могу сделать по этому поводу, или мне просто нужно оставить все символы библиотеки OpenSSL в глобальном пространстве имен?


Я просто подумал о попытке "использовать пространство имен OpenSSL" в файле-нарушителе после включения моего заголовка, но, к сожалению, вызывает такие ошибки, как:


/usr/include/openssl/x509v3.h:83:13: error: reference to ‘v3_ext_ctx’ is ambiguous
/usr/include/openssl/x509v3.h:71:8: error: candidates are: struct v3_ext_ctx
/usr/include/openssl/ossl_typ.h:160:16: error: struct OpenSSL::v3_ext_ctx

(Обратите внимание, что OpenSSL является библиотекой C, а не библиотекой С++, и поэтому исходные функции не находятся в каком-либо пространстве имен до тех пор, пока не будут добавлены в компиляцию С++. Моя техника рекомендуется Stroustrup в его книге "Язык программирования С++", Специальный выпуск. Из раздела 9.5 "Совет": "[8] # включить заголовки C в пространствах имен, чтобы избежать глобальных имен; §8.2.9.1, §9.2.2."

спросил(а) 2012-08-01T10:34:00+04:00 7 лет, 6 месяцев назад
1
Решение
86

Итак, проблема заключается в следующем: символы OpenSSL можно вводить только один раз; второй #include из них не будет иметь эффекта из-за включения охранников. Это означает, что они могут быть подключены к одному пространству имен внутри единицы компиляции.

Таким образом, если вы собираетесь работать с Boost.ASIO в своем блоке компиляции, который требует, чтобы они находились в глобальном пространстве имен, вам придется либо привести их в глобальное пространство имен самостоятельно (перед тем как включить #include < boost/asio.hpp > или пусть #include < boost/asio.hpp > делает это.

ответил(а) 2012-08-02T05:51:00+04:00 7 лет, 6 месяцев назад
72

В общем, это не сработает и не предполагается. Boost.Asio может (и должен) ожидать, что сможет ссылаться на типы OpenSSL как на глобальное пространство имен, например. ссылаясь на ::buf_mem_st, но это терпит неудачу, потому что вы вызвали это объявление как OpenSSL::buf_mem_st.


Кроме того, что произойдет, если <openssl/ssl.h> содержит другой заголовок, скажем <stddef.h> (который он делает), а затем определяет size_t как OpenSSL::size_t. Любой более поздний код, который включает <stddef.h>, не будет включать его снова, поскольку он содержит защитные макросы, и теперь ваша программа никогда не сможет использовать ::size_t, потому что она неверно объявлена ​​как OpenSSL::size_t - для многих реализаций это приведет к нарушению большей части стандарта С++ библиотека, если включена после включения openssl. В вашем случае возможно, что <cstdio> включил <stddef.h> в любом случае, но то же самое относится к любому стандарту (то есть не-OpenSSL) заголовку, включенному заголовками OpenSSL, например <sys/types.h>. Ваша программа определяет OpenSSL::pid_t и OpenSSL::off_t и OpenSSL::timeval и т.д.

Единственный способ исправить эту проблему состоит в том, чтобы включить каждый стандартный заголовок C перед тем, как сделать объект include-in-a-namespace, так что если OpenSSL пытается включить этот заголовок снова, он уже был включен правильно, в глобальное пространство имен. Даже тогда другие заголовки, ссылающиеся на OpenSSL (например, Boost.Asio), могут сломаться.


Просто скажи "нет".

ответил(а) 2012-08-01T11:04:00+04:00 7 лет, 6 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема