web-dev-qa-db-fra.com

Perl - Sous-programme redéfini

J'ai déjà posé cette question auparavant ou cherché et vu d'autres demander - pourquoi ai-je l'avertissement " Sous-programme mySub redéfini à l'adresse ../lib/Common.pm line x "? et vous obtenez toujours la réponse vous avez déclaré le sous-marin deux fois dans le même code J'ai créé ce package de test:

FICHIER ENTIER ---------------

package MyCommonPkg;

use strict;

sub thisSubroutineIsNotDefinedAnywhereElse{
}

1;

FICHIER ENTIER ---------------

et j'UTILISE ce package à partir d'un script Perl, qui utilise d'autres packages, utilisant également ce package, et j'obtiens l'avertissement suivant: 

Sous-programme ThisSubroutineIsNotDefinedAnywhereElse redéfini à l'adresse ../lib/MyCommonPkg.pm ligne 19.

Je promets que je n'ai déclaré ce sous-marin nulle part ailleurs. Alors, est-ce causé par une référence circulaire? Comment puis-je rechercher la cause de cet avertissement et la corriger?

25
user210757

Avez-vous une boucle de dépendance? Si Perl commence à compiler votre script et rencontre une ligne comme celle-ci:

use PackageA;

Perl met en pause la compilation de votre script; localise PackageA.pm et commence à le compiler. Si elle rencontre une ligne comme celle-ci:

use PackageB;

Perl met en pause la compilation de PackageA; localise PackageB.pm et commence à le compiler. Normalement, cela s'achèverait avec succès, et Perl reprendrait la compilation de PackageA et, une fois celle-ci terminée, le script reviendrait à la compilation de votre script et, une fois terminé, il commencerait à exécuter les opcodes compilés.

Cependant , si PackageB.pm contient cette ligne:

use PackageA;

Vous pourriez vous attendre à ce que cela ne fasse rien puisque Perl a déjà traité PackageA.pm, mais le problème est qu’il n’a pas encore fini. Donc, Perl mettra en pause la compilation de PackageB et commencera à compiler PackageA.pm à partir du début. Cela pourrait déclencher le message que vous voyez sur les sous-routines dans PackageA en cours de redéfinition.

En règle générale, deux packages ne doivent pas dépendre l'un de l'autre. Parfois, cependant, la boucle est plus difficile à localiser car elle est provoquée par un troisième package.

34
Grant McLean

Lorsque vous avez deux sous-routines portant le même nom dans des packages différents, vous devez voir cet avertissement (lorsque les avertissements sont activés) sous la forme "Sous-programme nouveau redéfini ...". La raison simple (ce qui est très proche de ce que Grant McLean a dit, mais pas tout à fait exactement) est que vous devez obliger vos paquets à ignorer la phase de compilation et à les exiger ensuite. De cette façon, le gestionnaire d’espaces de noms Perl ne trouvera pas de tels symboles en conflit portant le même nom lors de la compilation, et si vos modules n’ont aucune erreur, ils fonctionneront aussi très bien par la suite. 

Assurez-vous simplement de mettre en œuvre 

nécessite Module;

statement plutôt que 

utiliser le module; 

Vous ne devriez plus voir cet avertissement.

24
user1587276

Si vous êtes sur un système avec un système de fichiers ne respectant pas la casse (Windows et assez souvent OSX), et que vous faites use Common dans un fichier et use common dans un autre, vous pouvez causer des problèmes comme celui-ci.

7
hobbs

Cela ressemble à un problème causé par des dépendances circulaires. Voici comment le localiser. Si votre classe de problème ressemble à ceci:

package AlientPlanet;
use Dinosaurs;
sub has_dinosaurs {...}
1;

Puis changez votre exemple pour ressembler à ceci:

package AlienPlanet;
sub has_dinosaurs {...}     # <-- swap
use Dinosaurs;              # <-- swap
1;

Maintenant, compilez votre code avec Carp :: Always comme ceci:

⚡ Perl -MCarp::Always -c lib/AlienPlanet.pm                                                                                                            
Subroutine has_dinosaurs redefined at lib/AlienPlanet.pm line 4.
    require AlienPlanet.pm called at lib/Dinosaurs.pm line 4
    Dinosaurs::BEGIN() called at lib/AlienPlanet.pm line 4
    eval {...} called at lib/AlienPlanet.pm line 4
    require Dinosaurs.pm called at lib/AlienPlanet.pm line 5
    AlienPlanet::BEGIN() called at lib/AlienPlanet.pm line 4
    eval {...} called at lib/AlienPlanet.pm line 4
lib/AlienPlanet.pm syntax OK

Maintenant que vous avez un stacktrace, vous pouvez voir où se trouve la boucle. La solution rapide et sale consiste à utiliser Class :: Load in Dinosaurs.pm.

Pour une explication plus détaillée, essayez mon blog post .

4
Eric Johnson

Etes-vous par hasard en train d'exécuter ceci en tant que script cgi sur un serveur web?

Je constate que je dois redémarrer le serveur Web pour contourner cet avertissement.

2
Phluks

Regardez le programme package MyCommonPkg.pm et voyez ce qu'il dit. At-il quelque chose comme ça?

package MyCommonPkg;

use Exporter qw(import);   #  Might be "require" and not "use"
our @EXPORT = qw(thisSubroutineIsNotDefinedAnywhereElse);

La syntaxe peut être un peu différente. Les principales choses que vous devriez voir sont l'instruction package, le fait qu'elle utilise Exporter et que le tableau @EXPORT contient le nom de votre sous-routine.

Ce qui se passe est un conflit d'espaces de noms. Votre paquet définit le même sous-programme que vous définissez.

Pour éviter cela, Perl utilise espaces de noms. Par défaut, votre espace de noms est main. Cependant, les packages sont supposés définir leurs propres noms en utilisant la commande package.

L'espace de nom complet d'un sous-programme ou d'une variable correspond à l'espace de nom suivi de deux points, suivi du nom du sous-programme ou de la variable. Par exemple, si vous regardez File :: Find , vous verrez des références aux variables $File::Find::name et $File::Find::dir. Ce sont les variables $name et $dir dans le package File/Find.pm sous l'espace de noms File::Find.

Pour vous faciliter la tâche, les packages peuvent exporter leurs variables et leurs sous-routines dans votre espace de noms principal. Par exemple, si j'utilise File :: Copy , O peut le faire:

...
use File::Copy
...
copy ($file, $to_dir);

Au lieu de:

...
use File::Copy
...
File::Copy::copy ($file, $to_dir);

Si vous regardez File/Copy.pm, vous verrez ce qui suit:

package File::Copy;
...
our(@ISA, @EXPORT, @EXPORT_OK, $VERSION, $Too_Big, $Syscopy_is_copy);
...
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(copy move);

Le package File::Copy; définit un espace de noms. Le require Exporter; et la @ISA = qw(Exporter) permettent au package d'exporter des sous-routines et des variables dans l'espace de noms principal. Le @EXPORT importe automatiquement, sans rien vous dire, les sous-routines copy et move dans les main namespaceque vous le vouliez ou non!

Ce dernier bit est très important. Il est maintenant considéré que mauvaises manières utilise @EXPORT. Au lieu de cela, vous devriez utiliser @EXPORT_OK qui vous oblige à lister les sous-routines que vous voulez utiliser. Des paquets plus modernes comme Scalar :: Util do this.

Donc plusieurs choses. Tout d’abord, votre MyCommonPkg a-t-elle une instruction package MyCommonPkg;. Sinon, ça devrait. Cela empêche les variables et les sous-programmes des packages d’affecter votre programme de manière désagréable. Ensuite, vous pouvez utiliser @EXPORT ou @EXPORT_OK.

Si MyCommonPkg a une instruction package, utilise-t-il @EXPORT? Si tel est le cas, vous pouvez éviter ce problème de plusieurs manières:

  • Ignorer l'avertissement. C'est juste un avertissement. Puisque vous savez que vous redéfinissez le sous-programme et que vous souhaitez utiliser votre définition du sous-programme, ignorez-le.

Vous pouvez le faire pour désactiver l’avertissement lorsque vous redéfinissez le sous-programme:

use MyCommonPkg;

no warnings qw(redefine);
sub thisSubroutineIsNotDefinedAnywhereElse {
   ...
}
use warnings qw(redefine);
  • Utilisez require MyCommonPkg; au lieu de use MyCommonPkg;. Cela empêchera l'importation de any sous-routines ou de variables dans votre espace de noms, y compris ceux que vous souhaitez utiliser. Disons que MyCommonPkg définit quatre sous-programmes: thisSubroutineIsNotDefinedAnywhereElse, foo, bar et barfoo. Utiliser n'importe lequel de ces sous-programmes.

Vous devez faire ceci:

my $answer = MyCommonPkg::foo( $input );

Pas drôle.

  • Utilisez un autre nom pour votre sous-routine. Il aurait dû être documenté que ce sous-programme est défini dans MyCommonPkg et si vous souhaitez utiliser MyCommonPkg, vous ne devez pas utiliser de noms de sous-programmes exportés.

  • Enfin, si MyCommonPkg est relativement nouveau et n'est pas utilisé dans des dizaines de programmes, utilisez @EXPORT_OK au lieu de @EXPORT et assurez-vous que tous les programmes qui utilisent MyCommonPkg sont modifiés pour exporter les sous-routines qu'ils souhaitent:

Comme ça:

use MyCommonPkg qw(foo bar);

Dans ce cas, seuls les sous-programmes foo et bar sont exportés. Les sous-programmes thisSubroutineIsNotDefinedAnywhereElse et barfoo ne sont pas exportés dans votre environnement.

1
David W.

Assurez-vous de ne pas oublier cette ligne à la fin de votre module: 

1;

Je sais que cela a été inclus dans quelques exemples ici, mais je le mentionne parce que c'est facile à oublier, et dans mon cas, c'est la seule cause des erreurs!

0
todd412

J'ai eu le même problème; C'est parce que le programme utilisait un module et que le sous-programme était présent à la fois dans le programme et dans le module Perl;

0
Megiddo

J'ai essayé d'utiliser "package Common.pm" comme nom de package. Le compilateur m'a donné des erreurs. Très gentil de ça hein? Quelle version de Perl utilisez-vous? Je l'ai essayé sur 5.10.0 et 5.12.1. 

Même si vous pouvez compiler, il est recommandé de supprimer le fichier .pm. Par exemple;

Fichier: some_package.pm;

package some_package;
use strict;

sub yadayadayada { ... }

1;
0
J.J.