Почему вызов глобальной функции в статической вспомогательной функции класса шаблона приводит к ошибкам компоновщика, но не вызывает нестатические функции-члены?

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

Тестовая программа создается в IDE code::blocks. Пути, содержащие заголовки, были добавлены в каталоги поиска, а также добавлена ​​включенная библиотека.

Когда я компилирую свою тестовую программу, я получаю следующую ошибку:

obj/Debug/main.o: In function `givens':
<snip> undefined reference to `xhypot(double, double)'

Однако xhypot — это глобальная функция, объявленная во включенном заголовке в real.h как:

nr_double_t xhypot (const nr_double_t, const nr_double_t);

и определено в сопутствующем файле real.cpp.

nr_double_t xhypot (const nr_double_t a, const nr_double_t b) {
  nr_double_t c = fabs (a);
  nr_double_t d = fabs (b);
  if (c > d) {
    nr_double_t e = d / c;
    return c * sqrt (1 + e * e);
  }
  else if (d == 0)
    return 0;
  else {
    nr_double_t e = c / d;
    return d * sqrt (1 + e * e);
  }
}

Тип nr_double_t определен в config.h как:

/* The global type of double representation. */
#define nr_double_t double

Где происходит оскорбительный вызов xhypot, так это в статической вспомогательной функции для класса шаблона, например:

static nr_double_t
givens (nr_double_t a, nr_double_t b, nr_double_t& c, nr_double_t& s) {
  nr_double_t z = xhypot (a, b);
  c = a / z;
  s = b / z;
  return z;
}

Однако xhypot также вызывается в других нестатических функциях-членах класса с точно таким же синтаксисом (то есть xhypot(double,double), и если я заменю строку nr_double_t z = xhypot (a, b); в этом статическом методе на строку nr_double_t z = 0.0;, ошибка исчезнет.

Вот вся моя тестовая программа:

#include "config.h"
#include "m_trsolver.h"

int main (int argc, char ** argv)
{

    char infile[] = "test.net";
    m_trsolver the_m_trsolver;;

    return 0;

}

И полный лог сборки:

g++ -Wall -DHAVE_CONFIG_H  -g    -I../qucs/qucs-core -I../qucs/qucs-core/src -I../qucs/qucs-core/src/components -I../qucs/qucs-core/src/components/devices -I../qucs/qucs-core/src/components/digital -I../qucs/qucs-core/src/components/verilog -I../qucs/qucs-core/src/components/microstrip -I../qucs/qucs-core/src/converter -I../qucs/qucs-core/src/m-interface -I../qucs/qucs-core/src/math  -c /home/s0237326/src/qucs_m_interface_test/main.cpp -o obj/Debug/main.o
/home/s0237326/src/qucs_m_interface_test/main.cpp: In function ‘int main(int, char**)’:
/home/s0237326/src/qucs_m_interface_test/main.cpp:10: warning: unused variable ‘infile’
g++ -L../qucs/qucs-core -L../qucs/qucs-core/src -L../qucs/qucs-core/src/components -L../qucs/qucs-core/src/components/devices -L../qucs/qucs-core/src/components/digital -L../qucs/qucs-core/src/components/verilog -L../qucs/qucs-core/src/components/microstrip -L../qucs/qucs-core/src/converter -L../qucs/qucs-core/src/m-interface -L../qucs/qucs-core/src/math  -o bin/Debug/qucs_m_interface_test obj/Debug/main.o    ../qucs/qucs-core/src/libqucsatorfull.a 
obj/Debug/main.o: In function `givens':
/home/s0237326/src/qucs_m_interface_test/../qucs/qucs-core/src/eqnsys.cpp:1341: undefined reference to `xhypot(double, double)'
obj/Debug/main.o: In function `main':
/home/s0237326/src/qucs_m_interface_test/main.cpp:11: undefined reference to `m_trsolver::m_trsolver()'
/home/s0237326/src/qucs_m_interface_test/main.cpp:13: undefined reference to `m_trsolver::~m_trsolver()'
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 0 seconds)
3 errors, 1 warnings

Я использую code::blocks 10.05 в Scientific Linux 6.1 с gcc 4.4.6 и передаю -DHAVE_CONFIG_H, так как есть много

#if HAVE_CONFIG_H
# include <config.h>
#endif

В исходниках config.h генерируется autoconf. Я также должен добавить, что код отлично компилируется при сборке в виде программы. Я извлек часть кода этой программы (все, кроме одного файла), чтобы поместить ее в эту статическую библиотеку.

Класс шаблона распределен по двум файлам, eqnsys.h и eqnsys.cpp. Я знаю, что это не лучшая практика, вероятно, это было сделано, так как он довольно большой. Это не мой код. eqnsys.cpp включен в нижней части eqnsys.h ( #include "eqnsys.cpp"). Статическая вспомогательная функция определена в eqnsys.cpp. real.h обозначается #includeed сверху eqnsys.cpp, но не eqnsys.h, если это имеет значение. Добавьте следующее в начало eqnsys.cpp:

template class eqnsys<nr_complex_t>; 
template class eqnsys<nr_double_t>;

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


person crobar    schedule 24.05.2013    source источник
comment
Это не то, что означает определение. nr_double_t xhypot (const nr_double_t, const nr_double_t); — это просто объявление в C++, а #define nr_double_t double — это определение препроцессора. Однако компоновщик жалуется на m_trsolver, а не на nr_double_t.   -  person Luchian Grigore    schedule 24.05.2013
comment
Вам не хватает какого-то объектного файла или библиотеки?   -  person Some programmer dude    schedule 24.05.2013
comment
@LuchianGrigore, да, я ошибся в терминологии, я имею в виду, что xhypot объявляется как nr_double_t xhypot (const nr_double_t, const nr_double_t);, а тип nr_double_t определяется в config.h как double. Компоновщик жалуется на отсутствие m_trsolver, но первая ошибка связана с xhypot(double, double). Я не знаю, связаны ли они. Я отредактирую, чтобы внести пару исправлений.   -  person crobar    schedule 25.05.2013
comment
@RaymondChen, спасибо за ссылку, но, просматривая их, я не вижу точно такой же проблемы. Хотя, возможно, это мое непонимание.   -  person crobar    schedule 28.05.2013
comment
Невозможность компоновки соответствующих библиотек/объектных файлов или компиляции файлов реализации.   -  person Raymond Chen    schedule 28.05.2013
comment
@RaymondChen, да, но нет проблем со связыванием, кроме как в этой статической функции, и ошибка связана с функцией xhypot, которая определена в других источниках и работает в другом месте в том же классе шаблона, почему? В этом ответе это не объясняется, я прочитал много других вопросов о переполнении стека, но не видел такой же проблемы.   -  person crobar    schedule 28.05.2013
comment
Это в другом ответе: отсутствующая функция не та, которую вы определили. Вероятно, существует функция-член с именем xhypot, которую вызывают другие методы. Вы так и не сказали, где на самом деле определен xhypot. Запустите отладчик, чтобы увидеть, куда идут вызовы от нестатических членов.   -  person Raymond Chen    schedule 28.05.2013
comment
@RaymondChen, xhypot объявлено в файле real.h и определено в real.cpp, это глобальная функция. Он вызывается в других функциях-членах класса-шаблона с тем же синтаксисом, что и в этой статической функции-члене, то есть xhypot(double,double). Если я заменю строку nr_double_t z = xhypot (a, b); в статической функции-члене на nr_double_t z = 0.0, ошибка исчезнет. xhypot не является функцией-членом класса шаблона.   -  person crobar    schedule 28.05.2013
comment
Я отредактировал вопрос, чтобы сделать его более конкретным и потенциально полезным для других.   -  person crobar    schedule 28.05.2013
comment
@RaymondChen, я сделал ошибку, вызов происходит в статической вспомогательной функции, а не в функции-члене.   -  person crobar    schedule 28.05.2013
comment
Я не вижу real.o в журнале сборки. Я подозреваю, что функции, которые успешно вызывают xhypot, на самом деле никогда не вызываются.   -  person Raymond Chen    schedule 28.05.2013


Ответы (2)


Судя по командной строке сборки, вы только компилируете main.cpp и не связываете никакие библиотеки. Вы должны связать библиотеку, содержащую отсутствующие символы, или также скомпилировать их источники в свой исполняемый файл.

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

person Angew is no longer proud of SO    schedule 24.05.2013
comment
Библиотека, на которую я ссылаюсь, называется libqucsatorfull.a, что, как я думал, выполняется этой частью команды: -L../qucs/qucs-core/src/math -o bin/Debug/qucs_m_interface_test obj/Debug/main.o ../qucs/qucs-core/src/libqucsatorfull.a. Я неправильно понял? - person crobar; 25.05.2013
comment
@crobar Ты прав, я как-то пропустил эту часть строки. Виноват. Есть ли в библиотеке определение (тело) функции xhypot? - person Angew is no longer proud of SO; 25.05.2013
comment
@Andrew, да, я изначально ошибся, но только в том, что xhypot объявлен в заголовке real.h (а не в файле с именем eqnsys.h) и определенно имеет определение в соответствующем real.cpp. - person crobar; 25.05.2013
comment
@crobar Является ли real.cpp частью библиотеки? И является ли определение nr_double_t одинаковым как в real.cpp, так и в вашем тесте main.cpp? - person Angew is no longer proud of SO; 25.05.2013
comment
@ Андрей, да, real.cpp является частью библиотеки, а nr_double_t определяется в config.h, который является тем же config.h, включенным в мою программу (он создается для библиотеки с помощью autoconf). - person crobar; 25.05.2013
comment
@crobar Извините, у меня закончились идеи. Можете ли вы придумать минимальный полный пример и опубликовать его здесь? - person Angew is no longer proud of SO; 25.05.2013
comment
@ Эндрю, в любом случае спасибо, я посмотрю, смогу ли я сузить проблему. - person crobar; 25.05.2013
comment
@Andrew Эндрю, класс, в котором вызов xhypot, является классом-шаблоном, может ли это иметь значение? - person crobar; 26.05.2013

Почти наверняка это проблема создания экземпляра шаблона из-за того, что код вашего шаблона находится в .cpp, а не в .h.
Либо переместите свой код в заголовок, чтобы, когда он понадобится компилятору, он мог создать экземпляр шаблона для тип, требуемый на лету, или оставьте код в .cpp, но принудительно создайте экземпляр шаблона (в верхней части вашего файла .cpp) для нужного вам аргумента шаблона, добавив . Например:

// Explicite template instantiations
#pragma warning(disable: 4660) // For template-class specialization is already instantiated.
template class MyTemplateClass<MyTemplateParamClass>;
person Ricibob    schedule 28.05.2013
comment
Я добавил: template class eqnsys<nr_complex_t>; и template class eqnsys<nr_double_t>;, два типа, которые, как я знаю, будут использоваться, в начало eqnsys.cpp. Но я все еще получаю ту же ошибку. - person crobar; 28.05.2013