LINUX.ORG.RU

История изменений

Исправление firkax, (текущая версия) :

и есть пара вызовов A a; a.foo(). В одной либе и другой соответственно. Как, по твоему мнению, компоновщик должен узнать какую версию foo вызвать? Для компановщика и то и другое будет выглядеть как A::foo(A*) (опять же упрощенно).

Если после декорирования (mangling) у этих двух методов окажутся одинаковые имена - то будет конфликт имён. Но в данном случае вроде имена получатся разные и всё норм. Но про тип класса компоновщику знать ничего не нужно.

Звучит странно. В плюсах имена параметров являются частью name mangling

Я ж там уточнил про с++-декорирование. Да, оно тут решит проблему, но, повторю, его делает компилятор. Как раз для того, чтоб компоновщик мог тупо сравнить строки имён и больше ничего не делать.

А в Си это по идее не скомпилиться, там нет перегрузки.

Речь не про перегрузку, а про случай когда в двух разных модулях используются разные прототипы одной и той же функции. Для избежания такой ситуации принято инключить общие .h файлы со всеми прототипами - тогда компилятор может везде проверить что они совпадают, но инклюд .h можно и не делать, либо инключить разные .h - тогда компилятор ничего сравнить не сможет, а линкер - в любом случае не умеет. Опять оговорюсь, с++-декорирование в большинстве случаев приводит к устранению таких ситуаций, но можно сделать extern «C» или специально придумать две функции, на самом деле разные, но у которых декорированные имена совпадут.

Но сомневаюсь что будет работать sizeof(void *) + sizeof(char) != sizeof(int) как правило.

Работать полученный бинарник конечно будет некорректно (обычный итог - сегфолт). Но на этапе компиляции эту проблему никто не заметит.

В компановщиках давно используют всякие оптимизации типа LTO

LTO формально обрабатывается внутри компоновщика, но на самом деле это по сути часть компилятора, у него на входе не объектный (ассемблерный) код а какой-то промежуточный. Как бы то ни было, он сделан так чтобы на внешние проявления процесса компиляции по возможности не влиять, и речь не о нём вообще.

UPD всё оказалось хуже, декорированные имена методов совпадают и компоновщик выкинул второй foo() заменив его вызовом первого foo() для второго класса.

a.cpp

#include <iostream>

struct A {
  int c;
  void foo() { std::cout << "a::foo " << c << "\n"; }
};

int a1(void) {
  A a;
  a.c = 2;
  a.foo();
  return 10;
}
b.cpp
#include <iostream>

struct A {
  int b;
  int c;
  void foo() { std::cout << "b::foo " << b << " " << c << "\n"; }
  void foox() { std::cout << "b::foox " << b << " " << c << "\n"; }
};

int a2(void) {
  A a;
  a.b = 3;
  a.c = 4;
  a.foo();
  a.foox();
  return 11;
}
main.cpp
int a1();
int a2();
int main(void) {
  a1();
  a2();
  return 0;
}
$ g++ a.cpp b.cpp main.cpp
$ ./a.out 
a::foo 2
a::foo 3
b::foox 3 4

Исправление firkax, :

и есть пара вызовов A a; a.foo(). В одной либе и другой соответственно. Как, по твоему мнению, компоновщик должен узнать какую версию foo вызвать? Для компановщика и то и другое будет выглядеть как A::foo(A*) (опять же упрощенно).

Если после декорирования (mangling) у этих двух методов окажутся одинаковые имена - то будет конфликт имён. Но в данном случае вроде имена получатся разные и всё норм. Но про тип класса компоновщику знать ничего не нужно.

Звучит странно. В плюсах имена параметров являются частью name mangling

Я ж там уточнил про с++-декорирование. Да, оно тут решит проблему, но, повторю, его делает компилятор. Как раз для того, чтоб компоновщик мог тупо сравнить строки имён и больше ничего не делать.

А в Си это по идее не скомпилиться, там нет перегрузки.

Речь не про перегрузку, а про случай когда в двух разных модулях используются разные прототипы одной и той же функции. Для избежания такой ситуации принято инключить общие .h файлы со всеми прототипами - тогда компилятор может везде проверить что они совпадают, но инклюд .h можно и не делать, либо инключить разные .h - тогда компилятор ничего сравнить не сможет, а линкер - в любом случае не умеет. Опять оговорюсь, с++-декорирование в большинстве случаев приводит к устранению таких ситуаций, но можно сделать extern «C» или специально придумать две функции, на самом деле разные, но у которых декорированные имена совпадут.

Но сомневаюсь что будет работать sizeof(void *) + sizeof(char) != sizeof(int) как правило.

Работать полученный бинарник конечно будет некорректно (обычный итог - сегфолт). Но на этапе компиляции эту проблему никто не заметит.

В компановщиках давно используют всякие оптимизации типа LTO

LTO формально обрабатывается внутри компоновщика, но на самом деле это по сути часть компилятора, у него на входе не объектный (ассемблерный) код а какой-то промежуточный. Как бы то ни было, он сделан так чтобы на внешние проявления процесса компиляции по возможности не влиять, и речь не о нём вообще.

Исправление firkax, :

и есть пара вызовов A a; a.foo(). В одной либе и другой соответственно. Как, по твоему мнению, компоновщик должен узнать какую версию foo вызвать? Для компановщика и то и другое будет выглядеть как A::foo(A*) (опять же упрощенно).

Если после декорирования (mangling) у этих двух методов окажутся одинаковые имена - то будет конфликт имён. Но в данном случае вроде имена получатся разные и всё норм. Но про тип класса компоновщику знать ничего не нужно.

Звучит странно. В плюсах имена параметров являются частью name mangling

Я ж там уточнил про с++-декорирование. Да, оно тут решит проблему, но, повторю, его делает компилятор. Как раз для того, чтоб компоновщик мог тупо сравнить строки имён и больше ничего не делать.

А в Си это по идее не скомпилиться, там нет перегрузки.

Речь не про перегрузку, а про случай когда в двух разных модулях используются разные прототипы одной и той же функции. Для избежания такой ситуации принято инключить общие .h файлы со всеми прототипами - тогда компилятор может везде проверить что они совпадают, но инклюд .h можно и не делать, либо инключить разные .h - тогда компилятор ничего сравнить не сможет, а линкер - в любом случае не умеет. Опять оговорюсь, с++-декорирование в большинстве случаев приводит к устранению таких ситуаций, но можно сделать extern «C» или специально придумать две функции, на самом деле разные, но у которых декорированные имена совпадут.

Но сомневаюсь что будет работать sizeof(void *) + sizeof(char) != sizeof(int) как правило.

Работать полученный бинарнк конечно будет некорректно (обычный итог - сегфолт). Но на этапе компиляции эту проблему никто не заметит.

В компановщиках давно используют всякие оптимизации типа LTO

LTO формально обрабатывается внутри компоновщика, но на самом деле это по сути часть компилятора, у него на входе не объектный (ассемблерный) код а какой-то промежуточный. Как бы то ни было, он сделан так чтобы на внешние проявления процесса компиляции по возможности не влиять, и речь не о нём вообще.

Исправление firkax, :

и есть пара вызовов A a; a.foo(). В одной либе и другой соответственно. Как, по твоему мнению, компоновщик должен узнать какую версию foo вызвать? Для компановщика и то и другое будет выглядеть как A::foo(A*) (опять же упрощенно).

Если после декорирования (mangling) у этих двух методов окажутся одинаковые имена - то будет конфликт имён. Но в данном случае вроде имена получатся разные и всё норм. Но про тип класса компоновщику знать ничего не нужно.

Звучит странно. В плюсах имена параметров являются частью name mangling

Я ж там уточнил про с++-декорирование. Да, оно тут решит проблему, но, повторю, его делает компилятор. Как раз для того, чтоб компоновщик мог тупо сравнить строки имён и больше ничего не делать.

А в Си это по идее не скомпилиться, там нет перегрузки.

Речь не про перегрузку, а про случай когда в двух разных модулях используются разные прототипы одной и той же функции. Для избежания такой ситуации принято инключить общие .h файлы со всеми прототипами - тогда компилятор может везде проверить что они совпадают, но инклюд .h можно и не делать, либо инключить разные .h - тогда компилятор ничего сравнить не сможет, а линкер - в любом случае не умеет. Опять оговорюсь, с++-декорирование в большинстве случаев приводит к устранению таких ситуаций, но можно сделать extern «C» или специально придумать две функции, на самом деле разные, но у которых декорированные имена совпадут.

Но сомневаюсь что будет работать sizeof(void *) + sizeof(char) != sizeof(int) как правило.

Работать полученный бинарнки конечно будет некорректно (обычный итог - сегфолт). Но на этапе компиляции эту проблему никто не заметит.

В компановщиках давно используют всякие оптимизации типа LTO

LTO формально обрабатывается внутри компоновщика, но на самом деле это по сути часть компилятора, у него на входе не объектный (ассемблерный) код а какой-то промежуточный. Как бы то ни было, он сделан так чтобы на внешние проявления процесса компиляции по возможности не влиять, и речь не о нём вообще.

Исходная версия firkax, :

и есть пара вызовов A a; a.foo(). В одной либе и другой соответственно. Как, по твоему мнению, компоновщик должен узнать какую версию foo вызвать? Для компановщика и то и другое будет выглядеть как A::foo(A*) (опять же упрощенно).

Если после декорирования (mangling) у этих двух методов окажутся одинаковые имена - то будет конфликт имён. Но в данном случае вроде имена получатся разные и всё норм. Но про тип класса компоновщику знать ничего не нужно.

Звучит странно. В плюсах имена параметров являются частью name mangling

Я ж там уточнил про с++-декорирование. Да, оно тут решит проблему, но, повторю, его делает компилятор. Как раз для того, чтоб компоновщик мог тупо сравнить строки имён и больше ничего не делать.

А в Си это по идее не скомпилиться, там нет перегрузки.

Речь не про перегрузку, а про случай когда в двух разных модулях используются разные прототипы одной и той же функции. Для избежания такой ситуации принято инключить общие .h файлы со всеми прототипами - тогда компилятор может везде проверить что они совпадают, но инклюд .h можно и не делать, либо инключить разные .h - тогда компилятор ничего сравнить не сможет, а линкер - в любом случае не умеет. Опять оговорюсь, с++-декорирование в большинстве случаев приводит к устранению таких ситуаций, но можно сделать extern «C» или специально придумать две функции, на самом деле разные, но у которых декорированные имена совпадут.

В компановщиках давно используют всякие оптимизации типа LTO

LTO формально обрабатывается внутри компоновщика, но на самом деле это по сути часть компилятора, у него на входе не объектный (ассемблерный) код а какой-то промежуточный. Как бы то ни было, он сделан так чтобы на внешние проявления процесса компиляции по возможности не влиять, и речь не о нём вообще.