VC++: не работает packaging для независимых функций
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 27.10.21 18:31
Оценка:
Определяю простейшую функцию в заголовке, включаю заголовок в два разных .cpp-файла, компилирую с /Gy, линкую с /opt:icf — линкер стабильно ругается, что функция определена более одного раза. При добавлении параметра iterations к /opt:icf ничего не меняется.

В OBJ/ASM видно, что /Gy добавляет COMDAT, без него COMDAT не добавляется. Но линкер одинаково ругается, когда COMDATы есть, и когда их нет.

Таким же образом, как известно, оформляются функции-члены, определенные в определениях классов (которые обычно в заголовках) — с ними линкер отлично управляется.

Наблюдается как в старых версиях 15.xx, так и в самых новых 19.28.

Это такой многолетний глюк, или я чего не понимаю?
msvc vc++ packaged functions comdat
Re: VC++: не работает packaging для независимых функций
От: kov_serg Россия  
Дата: 27.10.21 19:13
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ>Определяю простейшую функцию в заголовке, включаю заголовок в два разных .cpp-файла,

А зачем вы так делаете.
Оставьте в заголовке только объявление функции. А реализацию затолкайте в отдельный файл.
Re[2]: VC++: не работает packaging для независимых функций
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 27.10.21 19:58
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>А зачем вы так делаете.


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

_>Оставьте в заголовке только объявление функции. А реализацию затолкайте в отдельный файл.


В данном случае функции общие для двух проектов, но недостаточно универсальные, чтобы выносить их в общую универсальную библиотеку. Я, конечно, могу их и заинлайнить, и скопировать, но и то, и другое будет уродливым костылем.
Re: VC++: не работает packaging для независимых функций
От: watchmaker  
Дата: 27.10.21 20:08
Оценка:
Здравствуйте, Евгений Музыченко, Вы писали:

ЕМ> Но линкер одинаково ругается, когда COMDATы есть, и когда их нет.


Как бы недвусмысленно намекает, что проблема вовсе не с секциями, не с настройками компилятора и не с настройками линкера. Значит остаётся проблема в коде.

ЕМ>Определяю простейшую функцию в заголовке, включаю заголовок в два разных .cpp-файла


Как именно? Может ты там ODR нарушаешь. Конечно, передача каких-то дополнительных опций для линкера в этом случае не сделает из такой программы правильную.

Предсказываю, тебе поможет один из вариантов:
(в порядке уменьшения предпочтения)
  1. Перенести определение в cpp, оставив в .h объявление. Функция с external linkage.
  2. Разрешить выбрать любое определение, написав inline. Функция с external weak linkage.
  3. Размножить функцию, написав static. Функция с internal linkage. (не рекомендую, это почти всегда пессимизация, добавил вариант просто для полноты).
Re[2]: VC++: не работает packaging для независимых функций
От: Евгений Музыченко Франция https://software.muzychenko.net/ru
Дата: 27.10.21 20:18
Оценка:
Здравствуйте, watchmaker, Вы писали:

ЕМ>>Определяю простейшую функцию


W>Как именно?


Да хоть как. Например, int f (int a) { return a; }

W>
  • Перенести определение в cpp, оставив в .h объявление. Функция с external linkage.

    Не хочу.

    Разрешить выбрать любое определение, написав inline.

    Тоже не хочу.

    W>Размножить функцию, написав static.


    И это не хочу. Хочу использовать документированную фичу. Если это глюк компилятора/линкера — накатаю багрепорт, нехай фиксят.
  • Re[3]: VC++: не работает packaging для независимых функций
    От: watchmaker  
    Дата: 27.10.21 20:29
    Оценка: +2
    Здравствуйте, Евгений Музыченко, Вы писали:

    ЕМ>Здравствуйте, watchmaker, Вы писали:


    ЕМ>>>Определяю простейшую функцию


    W>>Как именно?


    ЕМ>Да хоть как. Например, int f (int a) { return a; }


    Отлично, с этого и надо было начинать

    ЕМ>Хочу использовать документированную фичу. Если это глюк компилятора/линкера — накатаю багрепорт, нехай фиксят.


    Проблемы в компиляторе и в линкере тут нет. Они работают правильно и в согласии как со своей документацией, так и в согласии со стандартом языка С++.
    Проблема в твоём исходном коде.
    Re[4]: VC++: не работает packaging для независимых функций
    От: Евгений Музыченко Франция https://software.muzychenko.net/ru
    Дата: 27.10.21 20:39
    Оценка:
    Здравствуйте, watchmaker, Вы писали:

    W>Проблема в твоём исходном коде.


    А точнее?
    Re[3]: VC++: не работает packaging для независимых функций
    От: kov_serg Россия  
    Дата: 27.10.21 21:16
    Оценка:
    Здравствуйте, Евгений Музыченко, Вы писали:

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

    Вот если бы у вас имена вынимались из статической либы тогда бы работало. А так, если не нравиться штатные методы, у вас есть куча вариантов разной степени укуренности.
    static int add(int x,int y) { return x+y; }
    
    inline int add(int x,int y) { return x+y; }
    
    static inline int add(int x,int y) { return x+y; }
    
    namespace { int add(int x,int y) { return x+y; } }
    
    template<class T=void>
    int add(int x,int y) { return x+y; }
    
    struct add {
        int result;
        add(int x,int y) { result=x+y; }
        operator int() { return result; }
    };
    
    struct Utils {
        static int add(int x,int y) { return x+y; }
    };
    inline int add(int x,int y) { return Utils::add(x,y); } // либо полностью Utils::add(x,y) вместо add


    ЕМ>В данном случае функции общие для двух проектов, но недостаточно универсальные, чтобы выносить их в общую универсальную библиотеку. Я, конечно, могу их и заинлайнить, и скопировать, но и то, и другое будет уродливым костылем.

    Вообще не вижу в этом проблем, что бы вынести все вспомогательный функции в статическую либу.
    Отредактировано 27.10.2021 21:19 kov_serg . Предыдущая версия .
    Re[5]: VC++: не работает packaging для независимых функций
    От: watchmaker  
    Дата: 27.10.21 21:45
    Оценка: +2 -1
    Здравствуйте, Евгений Музыченко, Вы писали:

    W>>Проблема в твоём исходном коде.


    ЕМ>А точнее?


    Собирая программу из двух (или более) единиц трансляции, в каждой из которых написано это определение (непосредственно или через #include) ты нарушил one-definition-rule (ODR).

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

    Также обрати внимание, что перечисленные в исходном сообщении опции /Gy, /opt:icf являются по сути опциями оптимизации. Они не превращают неправильную программу, в которой уже допущена ошибка, в правильную.
    Re[6]: VC++: не работает packaging для независимых функций
    От: Евгений Музыченко Франция https://software.muzychenko.net/ru
    Дата: 28.10.21 05:41
    Оценка:
    Здравствуйте, watchmaker, Вы писали:

    W>Собирая программу из двух (или более) единиц трансляции


    Спасибо, мне это известно.

    W>вместо получения неопределённого поведения


    Я не делаю программу "в стандарте C++". Я делаю ее в соответствии с документацией на MS VC++. Поэтому соответствие стандарту меня сейчас совершенно не интересует.

    W>опции /Gy, /opt:icf являются по сути опциями оптимизации.


    Это меня тоже не интересует. Интересует только одно: правильно ли я использую эти опции. Если Вы считаете, что я использую их неправильно — поясните, пожалуйста, в чем именно.

    W>Они не превращают неправильную программу, в которой уже допущена ошибка, в правильную.


    Программа, в которой объекты данных определены более, чем в одной единице трансляции, тоже считается неправильной с точки зрения стандарта C++. Однако ж эти опции таки превращают ее в правильную. Почему?
    Re[4]: VC++: не работает packaging для независимых функций
    От: Евгений Музыченко Франция https://software.muzychenko.net/ru
    Дата: 28.10.21 05:44
    Оценка:
    Здравствуйте, kov_serg, Вы писали:

    _>у вас есть куча вариантов разной степени укуренности.


    Спасибо, но я это все хорошо знаю. Задача не в том, чтобы найти обходной путь, а в том, чтобы выяснить, соответствует поведение компилятора/линкера документации, или нет.
    Re[5]: VC++: не работает packaging для независимых функций
    От: kov_serg Россия  
    Дата: 29.10.21 06:48
    Оценка:
    Здравствуйте, Евгений Музыченко, Вы писали:

    ЕМ>Задача не в том, чтобы найти обходной путь, а в том, чтобы выяснить, соответствует поведение компилятора/линкера документации, или нет.

    Так в документации написано, что он убирает одинаковые функции. Не с одинаковыми именами, а с одинаковым телом.
    Если хочется что бы он мог выбирать из одинаковых имен, то их надо разместить в отдельных объектниках, но в разных статических библиотеках. И порядок подключения библиотек будет определять какую он выберет.
    Re[6]: VC++: не работает packaging для независимых функций
    От: Евгений Музыченко Франция https://software.muzychenko.net/ru
    Дата: 29.10.21 07:59
    Оценка:
    Здравствуйте, kov_serg, Вы писали:

    _>Так в документации написано, что он убирает одинаковые функции. Не с одинаковыми именами, а с одинаковым телом.


    Ну так у меня ж одна и та же функция, включаемая из того же самого заголовка в два разных файла. Как она может превратиться в две разных, если оба файла компилируются одним вызовом компилятора?
    Re[7]: VC++: не работает packaging для независимых функций
    От: kov_serg Россия  
    Дата: 29.10.21 08:54
    Оценка: -1
    Здравствуйте, Евгений Музыченко, Вы писали:

    ЕМ>Ну так у меня ж одна и та же функция, включаемая из того же самого заголовка в два разных файла. Как она может превратиться в две разных, если оба файла компилируются одним вызовом компилятора?

    Так сделайте её static и тогда линковщик возможно выкинет её реализации и оставит одну для этих функций. Но нафига так делать не очень ясно.
    Re[8]: VC++: не работает packaging для независимых функций
    От: Евгений Музыченко Франция https://software.muzychenko.net/ru
    Дата: 29.10.21 10:20
    Оценка:
    Здравствуйте, kov_serg, Вы писали:

    _>Так сделайте её static


    Сколько раз еще нужно повторить, что вопрос не в этом?
    Re[9]: VC++: не работает packaging для независимых функций
    От: kov_serg Россия  
    Дата: 29.10.21 10:52
    Оценка:
    Здравствуйте, Евгений Музыченко, Вы писали:

    _>>Так сделайте её static


    ЕМ>Сколько раз еще нужно повторить, что вопрос не в этом?

    А в чем?
    Re[10]: VC++: не работает packaging для независимых функций
    От: Евгений Музыченко Франция https://software.muzychenko.net/ru
    Дата: 29.10.21 11:12
    Оценка:
    Здравствуйте, kov_serg, Вы писали:

    ЕМ>>вопрос не в этом?

    _>А в чем?

    Я уже пояснял
    Автор: Евгений Музыченко
    Дата: 28.10.21
    .
    Re[11]: VC++: не работает packaging для независимых функций
    От: kov_serg Россия  
    Дата: 29.10.21 13:00
    Оценка:
    Здравствуйте, Евгений Музыченко, Вы писали:

    ЕМ>Я уже пояснял
    Автор: Евгений Музыченко
    Дата: 28.10.21
    .

    Так он и ведёт себя согласно документации.
    Re[7]: VC++: не работает packaging для независимых функций
    От: cserg  
    Дата: 29.10.21 15:56
    Оценка:
    Здравствуйте, Евгений Музыченко, Вы писали:

    ЕМ>Ну так у меня ж одна и та же функция, включаемая из того же самого заголовка в два разных файла.

    У вас две функции с одинаковым именем и телом.

    ЕМ>Как она может превратиться в две разных, если оба файла компилируются одним вызовом компилятора?

    Она не превращается, остаются две одинаковых с одинаковым именем. Опция OPT:ICF говорит линкеру свернуть одинаковые тела функций независимо от их имени, но дублирование имен при этом останется. Вот линкер на это и ругается, чтобы не ругался есть опция FORCE:MULTIPLE
    Re[8]: VC++: не работает packaging для независимых функций
    От: Евгений Музыченко Франция https://software.muzychenko.net/ru
    Дата: 29.10.21 21:18
    Оценка:
    Здравствуйте, cserg, Вы писали:

    C>Опция OPT:ICF говорит линкеру свернуть одинаковые тела функций независимо от их имени, но дублирование имен при этом останется.


    Фишка в том, что эта же технология (COMDAT) используется и для других подобных вещей. Например, inline-функции (как члены, так и независимые), которые по факту не инлайнятся, точно так же кладутся в COMDAT, и имена у них одни и те же в каждой единице трансляции, но на них линкер не ругается, а сворачивает тихо. Так же тихо он сворачивает и объекты данных, для которых указан __declspec (selectany). Вот и интересно, почему он не сворачивает таким же образом одинаковые функции с одинаковыми именами.

    C>Вот линкер на это и ругается, чтобы не ругался есть опция FORCE:MULTIPLE


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