C ++, стандартизирован порядок ссылок? - PullRequest
1 голос
/ 18 декабря 2009

На моем Linux-компьютере есть 2 библиотеки:

libfoo1.a and libfoo2.a

и они оба содержат реализацию

void foo(int)

и моя основная программа вызывает foo:

int main() { foo(1); return 0; }

Я скомпилировал программу двумя способами, используя g ++

g++ main.cpp libfoo1.a libfoo2.a -o a1.out
g++ main.cpp libfoo2.a libfoo1.a -o a2.out

Когда я запускаю программы, a1 явно использует реализацию foo () из libfoo1.a, в то время как a2 явно использует libfoo2. То есть g ++ связывается с тем foo (), который он видел первым.

Мой вопрос (наконец-то) заключается в том, действительно ли эта «жадная» политика компоновки указана в стандарте C ++? Или другой компилятор / платформа будет вести себя по-разному в зависимости от реализации?

PS: Чтобы поставить вопрос в практический контекст, мне действительно нравится, как работает этот пример g ++. В моем реальном приложении у меня есть устаревшая библиотека libfoo2, которая реализует много (много!) Функций, но я хочу предоставить новые реализации для нескольких из них в libfoo1. С одной стороны, я мог бы написать совершенно новый интерфейс в libfoo1, реализовать свою кучку, а затем делегировать libfoo2 для оставшейся части. Но я бы предпочел отказаться от написания всего этого кода делегирования, если бы я мог полагаться на линкеры, чтобы сделать это для меня (даже для компиляторов не-g ++, таких как icc).

PPS: Чтобы поместить это в реальный практический контекст, libfoo2 - это blas, а libfoo1 - это доморощенная реализация OpenMP некоторых из его подпрограмм. Я не готов раскошелиться на МКЛ. ATLAS не поддерживает многопоточность функций, которые я хочу вызвать. Он очень хорош в многопоточности GEMM, но мне нужно, чтобы некоторые из более причудливых подпрограмм из LAPACK тоже были быстрыми (zsptrf / zsptrs / zspr). Похоже, что моя неосведомленная о кеше реализация OpenMP этих подпрограмм может работать лучше, чем ее последовательная реализация с настройкой кеша.

Извините за длину сообщения.

Ответы [ 4 ]

3 голосов
/ 18 декабря 2009

Практически любой компоновщик будет вести себя так, как вы описали.

Традиционное поведение компоновщиков - поиск внешних функций слева направо в библиотеках, указанных в командной строке. Это означает, что библиотека, содержащая определение функции, должна появляться после любых исходных файлов или объектных файлов, которые ее используют.

С Введение в GCC .

3 голосов
/ 18 декабря 2009

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

2 голосов
/ 18 декабря 2009

В соответствии со стандартом у вас есть два определения одной и той же функции, что нарушает правило «Одно определение». Результатом является неопределенное поведение.

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

1 голос
/ 18 декабря 2009

Из того, что я могу сказать, стандарт C ++ не включает компоновщик. Вот почему, например, компиляторы Mingw32 и Microsoft C ++ могут обходиться без использования различных схем преобразования имен.

...