Question

J'ai trois fichiers d'en-tête dans mon projet qui décrivent des objets Rational, Complex, et RubyObject.Les deux premiers sont des modèles.Tous peuvent être interconvertis à l'aide de constructeurs de copie, qui sont définis dans les fichiers d'en-tête - à l'exception de ceux qui construisent Rational et Complex depuis const RubyObject&s, qui sont définis dans un fichier source.

Note: Ces définitions sont là par nécessité.Si ils tous allez dans les en-têtes, vous obtenez dépendance circulaire.

Il y a quelque temps, je suis tombé sur quelques erreurs de symboles non résolues avec les deux constructeurs de copie définis dans le fichier source.J'ai pu inclure dans le fichier source la fonction suivante

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

et puis appel nm_init_data() depuis le point d'entrée de la bibliothèque dans le fichier source principal.Cela a forcé ces symboles à être correctement liés.

Malheureusement, j'ai récemment mis à jour GCC et les erreurs sont de retour.En fait, cela semble se produire dans un endroit légèrement différent avec GCC 4.6 (par exemple, sur Travis-CI).

Mais ce n'est pas un problème spécifique à la version (comme je l'avais pensé auparavant).Nous le voyons sur Système basé sur Ubuntu de Travis CI, qui exécute GCC 4.6.Mais nous ne le voyons pas sur une machine Ubuntu avec GCC 4.8.1 ou 4.8.2.Mais nous faire voyez-le sur une machine Mac OS X avec 4.8.2 - et pas sur la même machine avec 4.7.2.Désactiver l'optimisation ne semble pas non plus aider.

Si je cours nm sur ma bibliothèque, le symbole est définitivement indéfini :

$ 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

Je ne sais pas pourquoi il existe deux entrées définies qui sont subordonnées au symbole non défini, mais je n'en sais pas non plus autant que je le souhaiterais sur les compilateurs.

Il semble également que le constructeur de copie soit un symbole non défini pour chaque version du Rational modèle:

__ZN2nm8RationalIiEC1ERKNS_10RubyObjectE
__ZN2nm8RationalIsEC1ERKNS_10RubyObjectE
__ZN2nm8RationalIxEC1ERKNS_10RubyObjectE

"Eh bien, c'est étrange", ai-je pensé."Complex64 et Complex128 sont également appelés en ce sens nm_init_data fonction, mais ils se résolvent tous les deux correctement - et ne sont pas répertoriés dans le nm -u sortie." J'ai donc essayé d'ajouter volatile avant la construction de la copie rationnelle également, pensant que peut-être le compilateur optimisait quelque chose que nous ne voulions pas optimiser.Mais cela n’a malheureusement pas résolu le problème non plus.C'est ce qui s'est produit, avec une mise en garde :

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

La mise en garde est que maintenant j'obtiens exactement la même erreur, mais pour les objets complexes à la place.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

C'est complètement absurde.Voici les définitions de ces deux fonctions, dans le même fichier source que le nm_init_data() fonction:

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

Indice: Une chose qui mérite d'être mentionnée est que l'erreur ne se produit pas lorsque nm_init_data() est appelé (c'est-à-dire lorsque la bibliothèque est chargée).Cela arrive bien plus tard, lors d’un nouvel appel à ces fonctions gênantes.

Comment puis-je résoudre ce problème une fois pour toutes, et d'autres l'aiment ?

Était-ce utile?

La solution

Vous prétendez ce qui suit, ce dont je doute.

Ces définitions sont là par nécessité.S'ils vont tous dans les en-têtes, vous obtenez une dépendance circulaire.

Dans la plupart des cas, vous pouvez résoudre un tel enchevêtrement circulaire en séparant votre code dans un fichier .hpp supplémentaire, qui est inclus avec la définition de classe qui contient les définitions de modèle partout où cela est nécessaire.

Si votre code a une véritable dépendance circulaire, il ne pourra pas être compilé.Habituellement, si vos dépendances semblent circulaires, vous devez regarder de plus près et descendre au niveau de la méthode et vérifier laquelle d'entre elles nécessiterait la compilation des deux types.

Il se peut donc que vos types s'utilisent les uns les autres, puis compilez le tout dans un seul fichier .cpp (par ex.via trois inclusions .hpp).Ou il n'y a qu'un pointeur vers un autre type, puis utilisez des déclarations forward pour vous assurer que tous les modèles sont résolus.Ou troisièmement, vous avez une méthode qui dépend de l'avant et d'autres qui dépendent de l'arrière, puis placez l'une dans un fichier, les autres dans un autre, et tout va bien à nouveau.

De plus, il semble que vous devriez utiliser une déclaration anticipée pour vos éléments manquants.Je m'attendrais à quelque chose comme ce qui suit après la définition de la fonction.Par exemple.:

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

Autres conseils

Rational, Complex...sont des modèles

copier les constructeurs...sont définis dans les fichiers d'en-tête - à l'exception de ceux qui construisent Rational et Complex depuis const RubyObject&s, qui sont définis dans un fichier source.

Et c'est là que réside votre problème.Depuis Rational et Complex sont des modèles, tous leurs méthodes doivent être disponibles dans votre fichier d'en-tête.

Si ce n'est pas le cas, vous pourrez parfois vous en sortir en fonction de l'ordre dans lequel les éléments sont appelés et de l'ordre dans lequel les éléments sont liés - mais le plus souvent, vous obtiendrez d'étranges erreurs concernant des symboles non définis, qui c'est exactement ce qui se passe ici.

Déplacez simplement les définitions de Rational(const RubyObject&) et Complex(const RubyObject&) dans les en-têtes respectifs et tout devrait fonctionner.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top