неопределенная ссылка на vtable с использованием нескольких пространств имен - PullRequest
0 голосов
/ 10 июня 2018

У меня эта странная проблема с связыванием уже несколько дней.У меня есть проект C ++ (в Ubuntu 16.04) с 2 пространствами имен.Каждое пространство имен имеет файлы .h и .cpp в отдельном каталоге, компилирующемся в файл библиотеки .a.В конце все связано в один исполняемый файл.

Проект довольно большой (модификация OpenBTS), поэтому, чтобы упростить его, это в основном то, что я пытаюсь сделать:

//*****directory A:

//file: A.h

namespace1 {

     class A {

         public:
         void functionA();
     };
}

//file: A.cpp

#include <B.h>
#include <vector.h>

using namespace1;
using namespace2;

void A::functionA (){
      B testB1;
      B testB2;
      testB2 = testB1; //works 
      testB1.functionB2(); //defined in B.h, works
      testB1.functionB(); //undefined reference to 'namespace2::B::functionB() const'
      std::vector<B> testvector;
      testvector.push_back(testB1); //undefined reference to 'namespace2::B'
}

//

//******directory B:

//file: B.h

#include "C.h"
//class C was part of the project before I started editing it and should not be the problem because other classes derived from it compile without problem.
namespace 2{
    class B: public C{
         int varB;
         public:
         B(){};
        ~B(){};

        void functionB() const; //overrides virtual functionB in class C
        int functionB2() { return varB;}
        void functionB2() //overrides pure virtual functionB2 in class C
    };
}

//file B.cpp

#include "B.h"
using namespace2

void B::functionB(){
  //code...
}

//main.cpp
//this creates an instance of A

В конце все файлы в каталоге A скомпилированы в файлы .o и затем связаны вместе в библиотеке Aa, то же самое для каталога B. Также main.cpp скомпилирован main.o

Затем все связаны: g ++ -g -O2 -Wall -pthread -rdynamic -o exename main.o ../B/.libs/Ba ../A/.libs/Aa -la53 -lzmq -pthread

Этогде я получаю ошибки:

неопределенная ссылка на 'namespace2 :: B' неопределенная ссылка на 'namespace2 :: B :: functionB () const'

Я уже проверил, все ли виртуальныефункции переопределяются в B, и это кажется ОК.Кроме того, когда я использую класс B внутри другого кода в namespace2, нет проблем, и все компилируется нормально.Вызов функции, определенной в Bh, работает, поэтому кажется, что компоновщик не может получить доступ к функциям, определенным в B.cpp?

Есть предложения?

Ответы [ 2 ]

0 голосов
/ 11 июня 2018

Я думаю, что это как-то связано с этой функцией, называемой text (std :: ostream &)

В Bh есть:

class B {
    ...
    void text(std::ostream&) const;
}

В B.cpp есть (завернутый вnamespace2):

void B::text(std::ostream& os) const {
    os << "some text";
}

Если я удаляю определение в B.cpp и объявляю и определяю в Bh:

class B {
    ...
    void text(std::ostream&) const {}; //no link problems!
}

Определение непосредственно в .h, и оно успешно скомпилируется.

Обратите внимание, что text () переопределяет виртуальную функцию из класса C, объявленную в Ch как:

virtual void text(std::ostream& os) const
    { os << "(no text())"; }

Я также пытался добавить виртуальную функцию перед функцией в Bh, но это нерешить проблему.

0 голосов
/ 10 июня 2018

При использовании пространств имен вы должны обернуть реализацию методов ваших классов в пространство имен также внутри вашего .cpp файла.Ваш a.cpp должен выглядеть примерно так:

//file: A.cpp

#include <B.h>
#include <vector.h>

namespace namespace1 {

using namespace2; // means 'look in namespace2' to the compiler

void A::functionA (){
      B testB1;
      B testB2;
      testB2 = testB1; 
      testB1.functionB2(); 
      testB1.functionB(); 
      std::vector<B> testvector;
      testvector.push_back(testB1); 
}

} // namespace1

, а ваш b.cpp должен выглядеть так:

//file B.cpp

#include "B.h"
namespace namespace2 {

void B::functionB() const{
  //code...
}

} // namespace2

Обратите внимание, что создание экземпляров объектов типа B работает, потому что конструкторопределяется внутри объявления класса.То же самое относится к B::function2().С другой стороны, A::functionA() и B::functionB() находятся в глобальном пространстве имен, а не в namespace1 и namespace2, как они должны.

Предложение using namespace_name; не определяет область действия внутри ваших файлов cpp.Он просто информирует компилятор о том, что нужно искать в этом пространстве имен символы, которые он найдет в этой единице перевода.

Ошибка, которую вы получаете при попытке заполнения вектора, возможно, связана с тем, что ваш типотсутствует реализация functionB(), потому что она находится в неправильном (глобальном) пространстве имен.Поэтому тип B является неполным и не может быть использован для создания экземпляра класса шаблона.

РЕДАКТИРОВАТЬ: В продолжение комментариев ниже, после некоторых проб и ошибок, оказалось, что на стороне кода все в порядке,но связывание не выполняется из-за циклических зависимостей между lib A и B, которые не очень хорошо видны в этом фиктивном примере, а также из-за других зависимостей, которым для работы необходим правильный порядок связывания.

Поэтому простое изменение порядка следования библиотек нежизнеспособно.Я советую избегать циклических зависимостей, если это возможно, или использовать специальные параметры ld - начальная группа и - конечная группа для решения проблем такого рода.Узнайте больше на страницах руководства gnu , связанных с ld.В Интернете вы найдете много примеров их использования.

Надеюсь, это поможет решить вашу проблему.

...