Отложенная привязка символов не удалась:символ не найден
-
20-12-2019 - |
Вопрос
У меня есть три файла заголовков в моем проекте которые описывают объекты Rational
, Complex
, и RubyObject
.Первые два — шаблоны.Все они могут быть преобразованы с помощью конструкторов копирования, которые определены в файлах заголовков, за исключением тех, которые создают Rational
и Complex
от const RubyObject&
s, которые определены в исходном файле.
Примечание: Эти определения существуют по необходимости.Если они все зайди в заголовки, получишь циклическая зависимость.
Некоторое время назад я столкнулся некоторые неразрешенные ошибки символов с двумя конструкторами копирования, определенными в исходном файле.Мне удалось включить в исходный файл следующую функцию
void nm_init_data() {
nm::RubyObject obj(INT2FIX(1));
nm::Rational32 x(obj);
nm::Rational64 y(obj);
nm::Rational128 z(obj);
volatile nm::Complex64 a(obj);
volatile nm::Complex128 b(obj);
}
а потом вызов nm_init_data()
из точки входа в библиотеку в основном исходном файле.Это заставило эти символы быть правильно связаны.
К сожалению, я недавно обновил GCC, и ошибки вернулись.На самом деле, в GCC 4.6 это происходит немного в другом месте. (например, на Travis-CI).
Но это не проблема конкретной версии (как я думал раньше).Мы видим это на Система Трэвиса CI на базе Ubuntu, который работает под управлением GCC 4.6.Но мы не видим этого на машине с Ubuntu ни с GCC 4.8.1, ни с 4.8.2.Но мы делать посмотрите это на машине Mac OS X с версией 4.8.2, а не на той же машине с 4.7.2.Отключение оптимизации тоже не помогает.
Если я побегу nm
в моей библиотеке символ определенно не определен:
$ nm tmp/x86_64-darwin13.0.0/nmatrix/2.0.0/nmatrix.bundle |grep RationalIsEC1ERKNS
U __ZN2nm8RationalIsEC1ERKNS_10RubyObjectE
00000000004ca460 D __ZZN2nm8RationalIsEC1ERKNS_10RubyObjectEE18rb_intern_id_cache
00000000004ca458 D __ZZN2nm8RationalIsEC1ERKNS_10RubyObjectEE18rb_intern_id_cache_0
Я не уверен, почему существуют две определенные записи, которые подчинены неопределенному символу, но я также не знаю столько, сколько хотелось бы о компиляторах.
Также похоже, что конструктор копирования является неопределенным символом для каждой версии Rational
шаблон:
__ZN2nm8RationalIiEC1ERKNS_10RubyObjectE
__ZN2nm8RationalIsEC1ERKNS_10RubyObjectE
__ZN2nm8RationalIxEC1ERKNS_10RubyObjectE
«Ну вот это странно», — подумал я."Complex64
и Complex128
здесь также называются nm_init_data
функция, но они обе разрешаются правильно — и не указаны в nm -u
вывод." Поэтому я попробовал добавить volatile
перед созданием копии Rational, думая, что, возможно, компилятор оптимизировал что-то, что мы не хотим оптимизировать.Но это, к сожалению, тоже не исправило ситуацию.Так и было, с оговоркой:
void nm_init_data() {
volatile VALUE t = INT2FIX(1);
volatile nm::RubyObject obj(t);
volatile nm::Rational32 x(const_cast<nm::RubyObject&>(obj));
volatile nm::Rational64 y(const_cast<nm::RubyObject&>(obj));
volatile nm::Rational128 z(const_cast<nm::RubyObject&>(obj));
volatile nm::Complex64 a(const_cast<nm::RubyObject&>(obj));
volatile nm::Complex128 b(const_cast<nm::RubyObject&>(obj));
}
Предостережение в том, что теперь я получаю ту же самую ошибку, но вместо сложных объектов.Ааа!
dyld: lazy symbol binding failed: Symbol not found: __ZN2nm7ComplexIdEC1ERKNS_10RubyObjectE
Referenced from: /Users/jwoods/Projects/nmatrix/lib/nmatrix.bundle
Expected in: flat namespace
dyld: Symbol not found: __ZN2nm7ComplexIdEC1ERKNS_10RubyObjectE
Referenced from: /Users/jwoods/Projects/nmatrix/lib/nmatrix.bundle
Expected in: flat namespace
Это полный абсурд.Вот определения обеих этих функций в том же исходном файле, что и nm_init_data()
функция:
namespace nm {
template <typename Type>
Complex<Type>::Complex(const RubyObject& other) {
// do some things
}
template <typename Type>
Rational<Type>::Rational(const RubyObject& other) {
// do some other things
}
} // end of namespace nm
Намекать: Стоит отметить, что ошибка не возникает, когда nm_init_data()
вызывается (т. е. когда библиотека загружается).Это происходит гораздо позже, во время очередного вызова этих неприятных функций.
Как мне решить эту проблему раз и навсегда, чтобы она понравилась другим?
Решение
Вы утверждаете следующее, в чем я сомневаюсь.
Эти определения существуют по необходимости.Если все они будут помещены в заголовки, вы получите циклическую зависимость.
В большинстве случаев такую циклическую путаницу можно решить, выделив код в дополнительный файл .hpp, который включается вместе с определением класса и содержит определения шаблонов в любом необходимом месте.
Если ваш код имеет реальную циклическую зависимость, он не сможет скомпилироваться.Обычно, если ваши зависимости кажутся циклическими, вам нужно присмотреться, спуститься на уровень метода и проверить, какой из них потребует компиляции обоих типов.
Таким образом, возможно, ваши типы используют друг друга, а затем компилируют все в один файл .cpp (например,через три .hpp-включения).Или есть только указатель на другой тип, тогда используйте упреждающие объявления, чтобы гарантировать, что все шаблоны разрешены.Или, в-третьих, у вас есть какой-то метод, который зависит от прямого, а какой-то от обратного, затем поместите один тип в один файл, другие — в другой, и все снова в порядке.
Кроме того, похоже, вам следует использовать предварительное объявление для недостающих элементов.Я ожидал бы чего-то вроде следующего после определения функции.Например.:
template nm::Complex<nm::RubyObject>::Complex(const nm::RubyObject& other);
Другие советы
Rational
,Complex
...шаблоныкопировать конструкторы...определены в файлах заголовков — за исключением тех, которые создают
Rational
иComplex
отconst RubyObject&
с, которые определены в исходном файле.
И в этом ваша проблема.С Rational
и Complex
это шаблоны, все их методы должны быть доступны в вашем заголовочном файле.
Если это не так, то иногда вам это сойдет с рук в зависимости от порядка вызова элементов и порядка их связывания, но чаще вы будете получать странные ошибки о неопределенных символах, которые это именно то, что здесь происходит.
Просто переместите определения Rational(const RubyObject&)
и Complex(const RubyObject&)
в соответствующие заголовки, и все должно работать.