Pergunta

Eu tenho três arquivos de cabeçalho no meu projeto que descrevem objetos Rational, Complex, e RubyObject.Os dois primeiros são modelos.Todos podem ser interconvertidos usando construtores de cópia, que são definidos nos arquivos de cabeçalho — exceto aqueles que constroem Rational e Complex de const RubyObject&s, que são definidos em um arquivo de origem.

Observação: Essas definições existem por necessidade.Se eles todos vá nos cabeçalhos, você obtém dependência circular.

Um tempo atrás, me deparei alguns erros de símbolos não resolvidos com os dois construtores de cópia definidos no arquivo de origem.Consegui incluir no arquivo fonte a seguinte função

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);
}

e então chamar nm_init_data() do ponto de entrada da biblioteca no arquivo de origem principal.Isso forçou esses símbolos a serem vinculados corretamente.

Infelizmente, atualizei recentemente o GCC e os erros voltaram.Na verdade, parece acontecer em um lugar um pouco diferente com o GCC 4.6 (por exemplo, no Travis-CI).

Mas não é um problema específico da versão (como pensei antes).Nós vemos isso Sistema baseado em Ubuntu do Travis CI, que executa o GCC 4.6.Mas não vemos isso em uma máquina Ubuntu com GCC 4.8.1 ou 4.8.2.Mas nós fazer veja-o em uma máquina Mac OS X com 4.8.2 - e não na mesma máquina com 4.7.2.Desativar a otimização também não parece ajudar.

Se eu correr nm na minha biblioteca, o símbolo é definitivamente indefinido:

$ 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

Não sei por que existem duas entradas definidas subordinadas ao símbolo indefinido, mas também não sei tanto quanto gostaria sobre compiladores.

Também parece que o construtor de cópia é um símbolo indefinido para cada versão do Rational modelo:

__ZN2nm8RationalIiEC1ERKNS_10RubyObjectE
__ZN2nm8RationalIsEC1ERKNS_10RubyObjectE
__ZN2nm8RationalIxEC1ERKNS_10RubyObjectE

“Bem, isso é estranho”, pensei."Complex64 e Complex128 também são chamados nisso nm_init_data funcionam, mas ambos resolvem corretamente - e não estão listados no nm -u saída." Então tentei adicionar volatile antes da construção da cópia do Rational também, pensando que talvez o compilador estivesse otimizando algo que não queremos otimizar.Mas isso também não resolveu, infelizmente.Isso aconteceu, com uma ressalva:

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));
}

A ressalva é que agora recebo exatamente o mesmo erro, mas para os objetos Complex.Argh!

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

Isto é completamente absurdo.Aqui estão as definições para ambas as funções, no mesmo arquivo fonte que o nm_init_data() função:

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

Dica: Uma coisa que vale a pena mencionar é que o erro não ocorre quando nm_init_data() é chamado (ou seja, quando a biblioteca é carregada).Isso acontece muito mais tarde, durante outra chamada para essas funções problemáticas.

Como faço para resolver esse problema de uma vez por todas e outros semelhantes?

Foi útil?

Solução

Você afirma o seguinte, o que duvido.

Essas definições existem por necessidade.Se todos eles estiverem nos cabeçalhos, você terá dependência circular.

Na maioria dos casos, você pode resolver esse emaranhado circular separando seu código em um arquivo .hpp adicional, que é incluído junto com a definição de classe que contém as definições de modelo em qualquer lugar necessário.

Se o seu código tiver uma dependência circular real, ele não poderá ser compilado.Normalmente, se suas dependências parecem circulares, você deve olhar mais de perto e descer ao nível do método e verificar quais delas exigiriam a compilação dos dois tipos.

Portanto, pode ser que seus tipos usem uns aos outros e compilem tudo em um arquivo .cpp (por exemplo,por meio de três inclusões .hpp).Ou há apenas um ponteiro para outro tipo e, em seguida, use declarações futuras para garantir que todos os modelos sejam resolvidos.Ou terceiro, você tem algum método que depende para frente e alguns que dependem para trás, então coloque um tipo em um arquivo, os outros em outro, e você está bem novamente.

Além disso, parece que você deve usar uma declaração antecipada para os itens ausentes.Eu esperaria algo parecido com o seguinte após a definição da função.Por exemplo.:

template nm::Complex<nm::RubyObject>::Complex(const nm::RubyObject& other);

Outras dicas

Rational, Complex...são modelos

copiar construtores...são definidos nos arquivos de cabeçalho - exceto aqueles que constroem Rational e Complex de const RubyObject&é, que são definidos em um arquivo de origem.

E é aí que reside o seu problema.Desde Rational e Complex são modelos, todos seus métodos precisam estar disponíveis em seu arquivo de cabeçalho.

Se não estiverem, às vezes você poderá se safar dependendo da ordem em que as coisas são chamadas e da ordem em que as coisas são vinculadas - mas com mais frequência você obterá erros estranhos sobre símbolos indefinidos, que é exatamente o que está acontecendo aqui.

Basta mover as definições de Rational(const RubyObject&) e Complex(const RubyObject&) nos respectivos cabeçalhos e tudo deve funcionar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top