Определяю простейшую функцию в заголовке, включаю заголовок в два разных .cpp-файла, компилирую с /Gy, линкую с /opt:icf — линкер стабильно ругается, что функция определена более одного раза. При добавлении параметра iterations к /opt:icf ничего не меняется.
В OBJ/ASM видно, что /Gy добавляет COMDAT, без него COMDAT не добавляется. Но линкер одинаково ругается, когда COMDATы есть, и когда их нет.
Таким же образом, как известно, оформляются функции-члены, определенные в определениях классов (которые обычно в заголовках) — с ними линкер отлично управляется.
Наблюдается как в старых версиях 15.xx, так и в самых новых 19.28.
Это такой многолетний глюк, или я чего не понимаю?
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Определяю простейшую функцию в заголовке, включаю заголовок в два разных .cpp-файла,
А зачем вы так делаете.
Оставьте в заголовке только объявление функции. А реализацию затолкайте в отдельный файл.
Re[2]: VC++: не работает packaging для независимых функций
Здравствуйте, kov_serg, Вы писали:
_>А зачем вы так делаете.
Это отдельный вопрос. Сейчас мне интересно, почему документированная фича не работает как минимум пятнадцать лет. И почему проблема только с независимыми функциями, когда для членов используется так же технология, и все работает.
_>Оставьте в заголовке только объявление функции. А реализацию затолкайте в отдельный файл.
В данном случае функции общие для двух проектов, но недостаточно универсальные, чтобы выносить их в общую универсальную библиотеку. Я, конечно, могу их и заинлайнить, и скопировать, но и то, и другое будет уродливым костылем.
Re: VC++: не работает packaging для независимых функций
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ> Но линкер одинаково ругается, когда COMDATы есть, и когда их нет.
Как бы недвусмысленно намекает, что проблема вовсе не с секциями, не с настройками компилятора и не с настройками линкера. Значит остаётся проблема в коде.
ЕМ>Определяю простейшую функцию в заголовке, включаю заголовок в два разных .cpp-файла
Как именно? Может ты там ODR нарушаешь. Конечно, передача каких-то дополнительных опций для линкера в этом случае не сделает из такой программы правильную.
Предсказываю, тебе поможет один из вариантов:
(в порядке уменьшения предпочтения) Перенести определение в cpp, оставив в .h объявление. Функция с external linkage.
Разрешить выбрать любое определение, написав inline. Функция с external weak linkage.
Размножить функцию, написав static. Функция с internal linkage. (не рекомендую, это почти всегда пессимизация, добавил вариант просто для полноты).
Re[2]: VC++: не работает packaging для независимых функций
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Здравствуйте, watchmaker, Вы писали:
ЕМ>>>Определяю простейшую функцию
W>>Как именно?
ЕМ>Да хоть как. Например, int f (int a) { return a; }
Отлично, с этого и надо было начинать
ЕМ>Хочу использовать документированную фичу. Если это глюк компилятора/линкера — накатаю багрепорт, нехай фиксят.
Проблемы в компиляторе и в линкере тут нет. Они работают правильно и в согласии как со своей документацией, так и в согласии со стандартом языка С++.
Проблема в твоём исходном коде.
Re[4]: VC++: не работает packaging для независимых функций
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Это отдельный вопрос. Сейчас мне интересно, почему документированная фича не работает как минимум пятнадцать лет. И почему проблема только с независимыми функциями, когда для членов используется так же технология, и все работает.
Вот если бы у вас имена вынимались из статической либы тогда бы работало. А так, если не нравиться штатные методы, у вас есть куча вариантов разной степени укуренности.
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
ЕМ>В данном случае функции общие для двух проектов, но недостаточно универсальные, чтобы выносить их в общую универсальную библиотеку. Я, конечно, могу их и заинлайнить, и скопировать, но и то, и другое будет уродливым костылем.
Вообще не вижу в этом проблем, что бы вынести все вспомогательный функции в статическую либу.
Здравствуйте, Евгений Музыченко, Вы писали:
W>>Проблема в твоём исходном коде.
ЕМ>А точнее?
Собирая программу из двух (или более) единиц трансляции, в каждой из которых написано это определение (непосредственно или через #include) ты нарушил one-definition-rule (ODR).
К счастью тебе повезло, и вместо получения неопределённого поведения (UB), линкер обнаружил эту проблему и написал о ней в сообщении об ошибке.
Также обрати внимание, что перечисленные в исходном сообщении опции /Gy, /opt:icf являются по сути опциями оптимизации. Они не превращают неправильную программу, в которой уже допущена ошибка, в правильную.
Re[6]: VC++: не работает packaging для независимых функций
Здравствуйте, watchmaker, Вы писали:
W>Собирая программу из двух (или более) единиц трансляции
Спасибо, мне это известно.
W>вместо получения неопределённого поведения
Я не делаю программу "в стандарте C++". Я делаю ее в соответствии с документацией на MS VC++. Поэтому соответствие стандарту меня сейчас совершенно не интересует.
W>опции /Gy, /opt:icf являются по сути опциями оптимизации.
Это меня тоже не интересует. Интересует только одно: правильно ли я использую эти опции. Если Вы считаете, что я использую их неправильно — поясните, пожалуйста, в чем именно.
W>Они не превращают неправильную программу, в которой уже допущена ошибка, в правильную.
Программа, в которой объекты данных определены более, чем в одной единице трансляции, тоже считается неправильной с точки зрения стандарта C++. Однако ж эти опции таки превращают ее в правильную. Почему?
Re[4]: VC++: не работает packaging для независимых функций
Здравствуйте, kov_serg, Вы писали:
_>у вас есть куча вариантов разной степени укуренности.
Спасибо, но я это все хорошо знаю. Задача не в том, чтобы найти обходной путь, а в том, чтобы выяснить, соответствует поведение компилятора/линкера документации, или нет.
Re[5]: VC++: не работает packaging для независимых функций
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Задача не в том, чтобы найти обходной путь, а в том, чтобы выяснить, соответствует поведение компилятора/линкера документации, или нет.
Так в документации написано, что он убирает одинаковые функции. Не с одинаковыми именами, а с одинаковым телом.
Если хочется что бы он мог выбирать из одинаковых имен, то их надо разместить в отдельных объектниках, но в разных статических библиотеках. И порядок подключения библиотек будет определять какую он выберет.
Re[6]: VC++: не работает packaging для независимых функций
Здравствуйте, kov_serg, Вы писали:
_>Так в документации написано, что он убирает одинаковые функции. Не с одинаковыми именами, а с одинаковым телом.
Ну так у меня ж одна и та же функция, включаемая из того же самого заголовка в два разных файла. Как она может превратиться в две разных, если оба файла компилируются одним вызовом компилятора?
Re[7]: VC++: не работает packaging для независимых функций
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Ну так у меня ж одна и та же функция, включаемая из того же самого заголовка в два разных файла. Как она может превратиться в две разных, если оба файла компилируются одним вызовом компилятора?
Так сделайте её static и тогда линковщик возможно выкинет её реализации и оставит одну для этих функций. Но нафига так делать не очень ясно.
Re[8]: VC++: не работает packaging для независимых функций
Здравствуйте, Евгений Музыченко, Вы писали:
ЕМ>Ну так у меня ж одна и та же функция, включаемая из того же самого заголовка в два разных файла.
У вас две функции с одинаковым именем и телом.
ЕМ>Как она может превратиться в две разных, если оба файла компилируются одним вызовом компилятора?
Она не превращается, остаются две одинаковых с одинаковым именем. Опция OPT:ICF говорит линкеру свернуть одинаковые тела функций независимо от их имени, но дублирование имен при этом останется. Вот линкер на это и ругается, чтобы не ругался есть опция FORCE:MULTIPLE
Re[8]: VC++: не работает packaging для независимых функций
Здравствуйте, cserg, Вы писали:
C>Опция OPT:ICF говорит линкеру свернуть одинаковые тела функций независимо от их имени, но дублирование имен при этом останется.
Фишка в том, что эта же технология (COMDAT) используется и для других подобных вещей. Например, inline-функции (как члены, так и независимые), которые по факту не инлайнятся, точно так же кладутся в COMDAT, и имена у них одни и те же в каждой единице трансляции, но на них линкер не ругается, а сворачивает тихо. Так же тихо он сворачивает и объекты данных, для которых указан __declspec (selectany). Вот и интересно, почему он не сворачивает таким же образом одинаковые функции с одинаковыми именами.
C>Вот линкер на это и ругается, чтобы не ругался есть опция FORCE:MULTIPLE
Она действует на все сразу. Я ж не хочу подавлять ошибки, вызванные ошибочным определением разных функций с одинаковыми именами.