Приводит ли использование только библиотек заголовков с разными версиями к UB - PullRequest
3 голосов
/ 13 марта 2019

Предположим, у меня есть библиотека somelib.a, которая распространяется менеджером пакетов как двоичная.И эта библиотека использует только библиотеку заголовков anotherlib.hpp.

Если я сейчас свяжу свою программу с somelib.a, а также использую anotherlib.hpp, но с другой версией, то это может привести к UB,если somelib.a использует части anotherlib.hpp в своих include заголовках.

Но что произойдет, если somelib.a будет ссылаться / использовать anotherlib.hpp только в своих файлах cpp (поэтому я незнаете что он их использует)?Будет ли шаг связывания между моим приложением и somelib.a гарантировать, что somelib.a и мое приложение будут использовать свою собственную версию anotherlib.hpp.

Причина, по которой я спрашиваю, заключается в том, чтобы связать отдельные единицы компиляции моегоПрограмма до конечной программы, затем компоновщик удаляет дубликаты символов (в зависимости от того, является ли это внутренней связью или нет).Таким образом, библиотека только для заголовков обычно пишется так, чтобы можно было удалить дубликаты символов.

Минимальный пример

somelib.a построен на системе с nlohmann/json.hpp версия 3.2

somelib / somelib.h

namespace somelib {
  struct config {
    // some members
  };

  config read_configuration(const std::string &path);
}

somelib.cpp

#include <nlohmann/json.hpp>


namespace somelib {
  config read_configuration(const std::string &path)
  {
     nlohmann::json j;
     std::ifstream i(path);

     i >> j;

     config c;

     // populate c based on j

     return c;
  }
}

приложение построено на другой системе с nlohmann / json.hpp версии 3.5 и 3.2 и 3.5 не совместимы, а затем приложение связывается с somelib.a, который был собран в системе с версией 3.2

application.cpp

#include <somelib/somelib.h>
#include <nlohmann/json.hpp>
#include <ifstream>

int main() {
   auto c = somelib::read_configuration("config.json");

   nlohmann::json j;
   std::ifstream i("another.json");

   i >> j;

   return 0;
}

1 Ответ

4 голосов
/ 13 марта 2019

Едва ли имеет значение, что вы используете статическую библиотеку.

Стандарт C ++ гласит, что если в программе имеется несколько определений встроенной функции (или шаблона класса, или переменной и т. Д.), И все определения не совпадают, то у вас есть UB .

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

Из окончательного рабочего проекта C ++ 17 (n4659.pdf) :

6.2 Правило единого определения

[...]

Может быть более одного определения типа класса (раздел 12), тип перечисления (10.2), встроенная функция с внешней связью (10.1.6), встроенная переменная с внешней связью (10.1.6), класс шаблон (пункт 17), шаблон нестатической функции (17.5.6), статический член данных шаблона класса (17.5.1.3), функция-член класса шаблон (17.5.1.1) или специализация шаблона, для которого некоторые параметры шаблона не указываются в программе при условии, что каждое определение появляется в другой единице перевода, и при условии, что определения удовлетворяют следующие требования.

Учитывая такую ​​сущность с именем D, определенную в более чем одном переводе единица, то

  • каждое определение D должно состоять из последовательность токенов; и

  • в каждом определении D, соответствующем имена, просмотренные в соответствии с 6.4, должны относиться к объекту, определенному в определении D, или должны относиться к тому же объекту, после разрешение перегрузки (16.3) и после сопоставления частичного шаблона специализация (17.8.3), за исключением того, что имя может относиться к (6.2.1)

    • энергонезависимый объект const с внутренней связью или без нее, если объект

      • имеет одинаковый литеральный тип во всех определениях D, (6.2.1.2)

      • инициализируется постоянным выражением (8.20),

      • не используется в любом определении D, а

      • имеет одинаковое значение во всех определениях D,

    или

    • ссылка с внутренней связью или без нее, инициализированная константным выражением так что ссылка относится к одному и тому же объекту во всех определениях из D; и (6,3)
  • в каждом определении D, соответствующие лица должны иметь одинаковые языковые связи; и

  • в каждом определении D, упомянутые перегруженные операторы, неявные вызовы функции преобразования, конструкторы, операторные новые функции и оператор удаления функций, должен относиться к той же функции, или к функция, определенная в определении D; и

  • в каждом определении D, аргумент по умолчанию, используемый (неявным или явным) вызовом функции обрабатывается так, как если бы его последовательность токенов присутствовала в определении D; то есть аргумент по умолчанию подчиняется требованиям описано в этом параграфе (и, если аргумент по умолчанию имеет подвыражения с аргументами по умолчанию, это требование применяется рекурсивно) .28

  • если D - класс с неявно объявленным конструктор (15.1), как если бы конструктор был неявно определен в каждой единице перевода, где она используется, и неявный определение в каждой единице перевода должен вызывать один и тот же конструктор для подобъекта D.

Если D является шаблоном и определен более чем в одной единице перевода, тогда предыдущие требования должны применяться как к именам из Область применения шаблона, используемая в определении шаблона (17.6.3),а также к зависимым именам в момент создания (17.6.2). Если определения D удовлетворяют всем этим требованиям, а затем поведение как если бы было одно определение D. Если определения D не удовлетворяют этим требованиям, то поведение не определено.

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