Код C ++ с условными аннотациями constexpr против ODR и компоновщика - PullRequest
0 голосов
/ 27 июня 2018

Я видел много библиотечного кода, который использует следующий шаблон для поддержки C ++ 11 / C ++ 14 / C ++ 17. Мне интересно понять, можно ли / почему / в какой степени это нормально в отношении «нарушения ODR» и проблем с компоновщиком.

Я ссылаюсь на фрагмент из библиотеки дат Говарда Хиннанта, который недавно был предложен для стандартизации.

https://github.com/HowardHinnant/date/blob/master/include/date/date.h

Сначала мы проверяем такие вещи, как _MSC_VER и __cplusplus, чтобы попытаться выяснить, на какой компилятор и стандарт C ++ мы нацеливаемся (в некоторых случаях это можно сделать только приблизительно), и определяем некоторые токены либо constexpr ключевое слово или пробел.

#if defined(_MSC_VER) && (!defined(__clang__) || (_MSC_VER < 1910))
// MSVC
#  if _MSC_VER < 1910
//   before VS2017
#    define CONSTDATA const
#    define CONSTCD11
#    define CONSTCD14
#    define NOEXCEPT _NOEXCEPT
#  else
//   VS2017 and later
#    define CONSTDATA constexpr const
#    define CONSTCD11 constexpr
#    define CONSTCD14 constexpr
#    define NOEXCEPT noexcept
#  endif

#elif defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x5150
// Oracle Developer Studio 12.6 and earlier
#  define CONSTDATA constexpr const
#  define CONSTCD11 constexpr
#  define CONSTCD14
#  define NOEXCEPT noexcept

#elif __cplusplus >= 201402
// C++14
#  define CONSTDATA constexpr const
#  define CONSTCD11 constexpr
#  define CONSTCD14 constexpr
#  define NOEXCEPT noexcept
#else
// C++11
#  define CONSTDATA constexpr const
#  define CONSTCD11 constexpr
#  define CONSTCD14
#  define NOEXCEPT noexcept
#endif

Затем многие функции-члены и тому подобные аннотируются с помощью этих макросов:

// date composition operators

CONSTCD11 year_month operator/(const year& y, const month& m) NOEXCEPT;
CONSTCD11 year_month operator/(const year& y, int          m) NOEXCEPT;

CONSTCD11 month_day operator/(const day& d, const month& m) NOEXCEPT;
CONSTCD11 month_day operator/(const day& d, int          m) NOEXCEPT;
CONSTCD11 month_day operator/(const month& m, const day& d) NOEXCEPT;
CONSTCD11 month_day operator/(const month& m, int        d) NOEXCEPT;
CONSTCD11 month_day operator/(int          m, const day& d) NOEXCEPT;

CONSTCD11 month_day_last operator/(const month& m, last_spec) NOEXCEPT;
CONSTCD11 month_day_last operator/(int          m, last_spec) NOEXCEPT;
CONSTCD11 month_day_last operator/(last_spec, const month& m) NOEXCEPT;
CONSTCD11 month_day_last operator/(last_spec, int          m) NOEXCEPT;

Теперь предположим, что у меня есть программа, которая состоит из нескольких библиотек, скомпилированных по стандарту C ++ 11, и некоторых библиотек, скомпилированных по стандарту C ++ 14, и многие из них включают этот файл и используют эти функции.

Это означает, что функции, отмеченные CONSTCD14, будут помечены constexpr в единицах перевода C ++ 14 и не будут помечены constexpr в единицах компиляции C ++ 11. Давайте предположим, что такие функции используются ODR в обоих типах единиц перевода.

  • Это нарушает ODR?
  • Является ли здесь ODR бессмысленным, поскольку стандартный документ относится только к стандарту на одном языке и не определяет совместимость между стандартами?
  • Влияет ли constexpr на искажение имени функции?
  • Должен ли я ожидать, что найдутся версии таких функций для C ++ 11 и C ++ 14, когда конечная программа связана, или я должен ожидать, что, если они встроены, компоновщик выберет либо C ++ 11, либо Версии C ++ 14, и они будут одинаковыми, если единственное отличие - аннотация constexpr?
  • Должен ли я ожидать, что подобные программы (в зависимости от одной и той же библиотеки при разных языковых стандартах, где аннотации constexpr могут различаться в зависимости от используемого языкового стандарта) "сработают", и эта схема компиляции одних и тех же заголовков в разных языковых стандартах как незначительный технический долг, или эту ситуацию следует рассматривать как ошибку?

Ответы [ 3 ]

0 голосов
/ 07 июля 2018

ODR является гарантией строгой эквивалентности сущностей с одинаковыми именами в разных TU (единицы перевода). Эквивалентность, используемая каждым компилятором, не ясна. (Я даже не знаю, документируют ли это авторы компилятора.)

Некоторые варианты ODR применимы к любой многоязычной программе (или к программе с несколькими объектными файлами компилятора C ++), если только уровень компоновщика. На этом уровне очевидно, что «имена» сущностей - это не квалифицированные C ++ имена объектов и сигнатуры перегруженных функций C ++, а искаженные имена. Вы должны проверить, что ABI согласен столько, сколько необходимо (если вы используете только глобальные объекты инициализации и не полиморфный объект или шаблон между компиляторами, они не должны соглашаться во всем).

Смысл ODR заключается в том, что любое использование имени с внешней связью несколько эквивалентно в любом TU; в частности, вызов функции, которая является встроенной или шаблонной, детерминистически выполняет операции, описанные в теле функции, видимой в TU (вплоть до неотъемлемого недетерминизма кода функции).

0 голосов
/ 10 июля 2018

Мои $ .02:

Это нарушает ODR?

Строго говоря, я полагаю, да, но на практическом уровне это, вероятно, не имеет значения, особенно с конкретной ссылкой на constexpr.

Если функция объявлена ​​как constexpr, то компилятор гарантированно сгенерирует постоянную времени компиляции, поэтому тело функции никогда не нужно генерировать, и все.

Для TU, скомпилированных с C ++ 11, ODR будет удовлетворен для всех этих TU, но я все равно ожидал бы, что результат вызова функции будет постоянной времени компиляции (поскольку очевидно, что семантика не изменилась и компилятор может генерировать эту константу при компиляции для C ++ 14, так почему же это не может быть здесь?).

Другими словами, constexpr не говорит компилятору «скомпилировать этот код как константу времени компиляции» (компилятор всегда будет делать все возможное, чтобы сделать это в любом случае). Вместо этого он говорит: «скажи мне, если не сможешь», и, следовательно, гарантирует потребителям то, к чему применяется constexpr, что на самом деле это то, что он утверждает.

Является ли здесь ODR бессмысленным, поскольку стандартный документ относится только к стандарту на одном языке и не определяет совместимость между стандартами?

Может быть, мы просто немного изменили правила.

Влияет ли constexpr на искажение имени функции?

как классификатор возвращаемого значения? Нет.

Должен ли я ожидать, что найдутся версии таких функций для C ++ 11 и C ++ 14, когда конечная программа связана, или я должен ожидать, что, если они встроены, компоновщик выберет либо C ++ 11, либо Версии C ++ 14, и они будут одинаковыми, если единственное отличие - аннотация constexpr?

Как я полагаю, мы уже установили, что не будет никаких функциональных тел, и можно ожидать, что константы времени компиляции, сгенерированные обеими версиями, будут одинаковыми. Я также ожидал бы, что сгенерированный код будет таким же, так как constexpr ничего не добавляет к тому, что компилятор уже выяснил для себя.

Должен ли я ожидать, что подобные программы (в зависимости от одной и той же библиотеки при разных языковых стандартах, где аннотации constexpr могут различаться в зависимости от используемого языкового стандарта) "сработают", и эта схема компиляции одних и тех же заголовков в разных языковых стандартах как незначительный технический долг или эту ситуацию следует рассматривать как ошибку?

Учитывая вышесказанное, я ожидаю, что это сработает. -std=C++x больше о том, какой синтаксис компилятор примет и не примет, чем что-либо еще (хотя, очевидно, есть исключения). Посмотри на это. Если бы вы не могли смешивать и сопоставлять библиотеки так, как вы описали, поставщики компиляторов были бы очень непопулярными, поэтому я полагаю, что они потратили время, чтобы обеспечить, насколько это возможно, Вы можете.

А как же noexcept. Что ж, если компилятор просто генерирует константы времени компиляции по всем направлениям, то очевидно, что ни одна из этих функций не может сработать, поэтому я думаю, что мы можем забыть об этом здесь.

Как проверить предположения, сделанные в этом посте

Запустите какую-нибудь тестовую программу и проверьте код на Godbolt для C ++ 14 и C ++ 11. Я не могу сделать это отсюда, потому что это непригодно для планшета: (

0 голосов
/ 28 июня 2018

ODR здесь не имеет смысла, потому что нет гарантии, что вы можете скомпилировать код с разными версиями языка C ++ в один двоичный файл и заставить его работать как положено.

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

Изменения такого рода перечислены в приложении «Совместимость» спецификации языка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...