IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Delphi Discussion :

"Appel surchargé ambigu" étonnant ?


Sujet :

Langage Delphi

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut "Appel surchargé ambigu" étonnant ?
    Bonjour,

    J'ai un problème avec un bout de code, je vais peut-être trouver la solution par moi-même avant de l'avoir ici mais comme ça me semble bizarre je le poste pour info :

    J'effectue un appel d'une méthode dont il existe 4 déclarations différentes et à la compilation j'ai une erreur le compilateur "Appel surchargé ambigu" alors que de mon point de vue l'appel n'est pas ambigu et peut-être résolu. Du coup je sais pas trop comment la corriger "proprement" (sans ajouter un argument inutile ou ce genre de bidouille dont je ne suis pas fan).

    Un screenshoot vous expliquera le problème :



    Quelqu'un a une idée ? Merci d'avance :-) !

  2. #2
    Membre éprouvé
    Avatar de Dr.Who
    Inscrit en
    Septembre 2009
    Messages
    980
    Détails du profil
    Informations personnelles :
    Âge : 45

    Informations forums :
    Inscription : Septembre 2009
    Messages : 980
    Points : 1 294
    Points
    1 294
    Par défaut
    appel ambigue avec les deux array, crée des types TArray...
    comme cela au pire des cas, tu pourra faire des appel explicite TArray...([...]);
    ça viens surement de la.
    [ Sources et programmes de Dr.Who | FAQ Delphi | FAQ Pascal | Règlement | Contactez l'équipe ]
    Ma messagerie n'est pas la succursale du forum... merci!

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par Dr.Who Voir le message
    appel ambigue avec les deux array, crée des types TArray...
    comme cela au pire des cas, tu pourra faire des appel explicite TArray...([...]);
    ça viens surement de la.
    Merci Dr. Who. Mais si je déclare des 'TXXXDynArray' à la place (solution que j'ai trouvé en attendant) ma méthode ne supporte plus que je lui passe des constructeurs de tableaux en argument (les appels "TArray...([...])" ne peuvent pas fonctionner. Je suis obligé à la place d'avoir une fonction "ToXXXDynArray" qui recréé un tableau dynamique de même type de base (ce que j'avais déjà fait également).
    En effet la syntaxe "array of XXX" pour un argument en Delphi a une signification bien particulière : "argument de type tableau ouvert" alors qu'un argument de type "TXXXDynArray" est un argument de tableau dynamique tout simplement.
    En fait, pour l'instant j'ai redéfini mes méthodes avec des arguments de tableaux dynamiques partout, en passant chaque constructeurs grâce à la méthode "ToXXXDynArray". C'est super fastidieux car j'ai de nombreux appels (et pas vraiment moyen de les retrouver avec les expressions régulières)...

    J'aurai juste aimé savoir s'il n'y avait pas une solution plus simple, qui permet au compilateur de lever l'ambiguïté. Car de mon point de vue il ne devrait pas s'arrêter au type "array of" mais regarder le type de base du tableau pour discriminer ces appels. Ce qu'il semble pourtant ne pas faire...

  4. #4
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    personnellement j'ai toujours préféré des noms de fonctions différents

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    function GetItemByName(const name: string):TItem;
    function GetItemByIndex(Index:Integer):TItem;
    au lieu de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    function GetItem(const name: string):TItem; overload;
    function GetItem(Index:Integer):TItem; overload;
    ou encore comme sous OpenGL
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    procedure glVertex2i(x, y: Integer);
    procedure glVertex3i(x, y, z: Integer);
    procedure glVertex2f(x, y: Single);
    procedure glVertex3f(x, y, z: Single);
    procedure glVertex2d(x, y: Double);
    procedure glVertex3d(x, y, z: Double);
    au lieu de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    procedure glVertex(x, y: Integer); overload;
    procedure glVertex(x, y, z: Integer); overload;
    procedure glVertex(x, y: Single); overload;
    procedure glVertex(x, y, z: Single); overload;
    procedure glVertex(x, y: Double); overload;
    procedure glVertex(x, y, z: Double); overload;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    personnellement j'ai toujours préféré des noms de fonctions différents

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    function GetItemByName(const name: string):TItem;
    function GetItemByIndex(Index:Integer):TItem;
    au lieu de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    function GetItem(const name: string):TItem; overload;
    function GetItem(Index:Integer):TItem; overload;
    ou encore comme sous OpenGL
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    procedure glVertex2i(x, y: Integer);
    procedure glVertex3i(x, y, z: Integer);
    procedure glVertex2f(x, y: Single);
    procedure glVertex3f(x, y, z: Single);
    procedure glVertex2d(x, y: Double);
    procedure glVertex3d(x, y, z: Double);
    au lieu de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    procedure glVertex(x, y: Integer); overload;
    procedure glVertex(x, y, z: Integer); overload;
    procedure glVertex(x, y: Single); overload;
    procedure glVertex(x, y, z: Single); overload;
    procedure glVertex(x, y: Double); overload;
    procedure glVertex(x, y, z: Double); overload;
    Ouaip, c'est peut-être plus rigoureux, encore que... j'ai plutôt la logique de ne rien faire d'inutile et d'avoir donc le code "le plus simple". Suivant ce raisonnement je ne vois pas spécialement d'intérêt à faire cela.
    Et s'il arrive qu'en passant un entier dans une méthode qui peut attendre soit un entier, soit un "flottant" on se pose la question de "est-ce que je veux bien appeler la méthode avec l'entier ?" c'est que les méthodes ont été mal définies à la base. On ne devrait pas avoir à se poser la question. Par exemple dans ton cas OpenGL c'est ainsi.
    Le cas foireux serait celui ou tu passes un argument entier, et les autres "flottants" par exemple, mais c'est un cas foireux, qui n'arrive que si on est pas rigoureux dans le choix de ses variables.utilisation des méthodes.

    Et dans mon cas l'idée n'était pas réellement d'avoir des arguments différents, mais de proposer des arguments facultatifs (qui auraient comme valeur 'nil' par défaut), sauf que pour des argument de type "tableaux ouverts" Delphi ne le permet pas.

  6. #6
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    c'est une question de goût...personnellement je préfère savoir ce que j'invoque que de laisser le compilateur choisir pour moi. Et le compilateur se charge alors de convertir les arguments vers ce qui est attendu...ça évite forcer le type des paramètres pour correspondre à la signature.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    procedure SendByte(Value: Byte);
    procedure SendWord(Value: Word);
     
    var
     i : Integer;
    begin
      if i < $7F then 
        SendByte(i) 
      else 
        SendWord(i); // exemple bidon
    end;
     
    // ou
     
    procedure Send(Value: Byte); overload;
    procedure Send(Value: Word); overload;
     
    var 
      i : Integer;
    begin
      if i < $7F then
        Send(Byte(i))
      else
        Send(Word(i));
    end;
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par Paul TOTH Voir le message
    c'est une question de goût...personnellement je préfère savoir ce que j'invoque que de laisser le compilateur choisir pour moi. Et le compilateur se charge alors de convertir les arguments vers ce qui est attendu...ça évite forcer le type des paramètres pour correspondre à la signature.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     
    procedure SendByte(Value: Byte);
    procedure SendWord(Value: Word);
     
    var
     i : Integer;
    begin
      if i < $7F then 
        SendByte(i) 
      else 
        SendWord(i); // exemple bidon
    end;
     
    // ou
     
    procedure Send(Value: Byte); overload;
    procedure Send(Value: Word); overload;
     
    var 
      i : Integer;
    begin
      if i < $7F then
        Send(Byte(i))
      else
        Send(Word(i));
    end;
    Comme tu dis c'est une question de point de vue. Perso je profite du fait que le langage soit fortement typé pour ne justement pas avoir à "expliciter" les types dans les prototypes des fonctions.
    Dans certains cas particuliers effectivement (le tiens) tu es obligé de préciser mais je ne tiens à généralement pour ce que je pense être des cas exceptionnels. A mon sens avec un langage fortement "c'est plus logique"...

  8. #8
    Membre éprouvé
    Avatar de Dr.Who
    Inscrit en
    Septembre 2009
    Messages
    980
    Détails du profil
    Informations personnelles :
    Âge : 45

    Informations forums :
    Inscription : Septembre 2009
    Messages : 980
    Points : 1 294
    Points
    1 294
    Par défaut
    il y a des cas ou l'on ne peux pas avoir plusieur identifiant de fonction,

    un astuce consiste de passer par un Flag global de type à transmettre par pointeur :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    const
      XFlag_A_Int = $0001;
      XFlag_A_Float = $0002;
      XFlag_A_Array = $0010;
     
      XFlag_B_Int = $0100;
      XFlag_B_Float = $0200;
      XFlag_B_Array = $1000;
     
      XFlag_AB_Int = XFlag_A_Int or XFlag_B_Int;
      XFlag_AB_Float = XFlag_A_Float or XFlag_B_Float;
      XFlag_AB_Array = XFlag_A_Array or XFlag_B_Array;
     
    function X(A, B: pointer; Flag: word);
     
    // appels à X :
     
    x(@AI, @BF, XFlag_A_Int or XFlag_B_Float);
    x(@AAF, @BAI, XFlag_A_Float or XFlag_B_Int or XFlag_AB_Array); 
    x(@AF, nil, XFlag_A_Float);
    [ Sources et programmes de Dr.Who | FAQ Delphi | FAQ Pascal | Règlement | Contactez l'équipe ]
    Ma messagerie n'est pas la succursale du forum... merci!

  9. #9
    Membre du Club
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2005
    Messages : 149
    Points : 61
    Points
    61
    Par défaut
    Citation Envoyé par Dr.Who Voir le message
    il y a des cas ou l'on ne peux pas avoir plusieur identifiant de fonction,

    un astuce consiste de passer par un Flag global de type à transmettre par pointeur :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    const
      XFlag_A_Int = $0001;
      XFlag_A_Float = $0002;
      XFlag_A_Array = $0010;
     
      XFlag_B_Int = $0100;
      XFlag_B_Float = $0200;
      XFlag_B_Array = $1000;
     
      XFlag_AB_Int = XFlag_A_Int or XFlag_B_Int;
      XFlag_AB_Float = XFlag_A_Float or XFlag_B_Float;
      XFlag_AB_Array = XFlag_A_Array or XFlag_B_Array;
     
    function X(A, B: pointer; Flag: word);
     
    // appels à X :
     
    x(@AI, @BF, XFlag_A_Int or XFlag_B_Float);
    x(@AAF, @BAI, XFlag_A_Float or XFlag_B_Int or XFlag_AB_Array); 
    x(@AF, nil, XFlag_A_Float);
    Dans ce cas il faut Delphi 2009 minimum et utiliser les generics ;-)

  10. #10
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 421
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 421
    Points : 24 776
    Points
    24 776
    Par défaut
    J'ai eu le même problème que toi, à force de faire des méthodes surchargées avec le même nom, dans mon cas, c'était une fonction Select qui générait du SQL à la volée pour une couche de persistance DB (la même chose que toi non ?), j'avais ce genre d'ambiguité parce que je ne veillais pas à conserver toujours le même ordre des paramètres entre les variantes très proches
    En fait mon code ressemblait fortement au tien

    Soit je faisais comme PaulToth, un nommage plus spécifique
    Soit je mettait explicitement touts les paramètres optionnelles qui permettait la différenciation de la fonction
    Soit je changeais l'ordre des paramètres pour éviter les confusions

    Dans la 1ere forme, AWFieldType en 5 et AWReturnBinds en 6
    Dans la 4eme forme, AWReturnBinds en 5
    As-tu tenté d'inversé AWFieldType après AWReturnBinds dans la première forme pour que AWReturnBinds soit toujours en 5, ainsi AWFieldType en 6 serait comparé à AExtraStatment ce qui normalement ne devrait posé aucun soucis d'ambiguïté

    Au Final, je préférais ajouter des classes pour éviter ces fonctions à ralongent, certe il faut plus de code pour l'affectation et encore plus pour gérer les propriétés tableaux mais le code est nettement plus maintenable et surtout les évolutions se font plus simplement avec des classes

    Exemple de code appelant (qui te laisse imaginer le code interne)
    On arrive à un code proche de celui de ZendDB mais aussi SQL que LinQ


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    with qq.Parameters() do
    begin
      TableName := CST_BDD_INTERV;
      SelectFields.All(); // ta constante c'est genre ... = ('*');
      // SelectFields.Add('Champ1'); // SelectFields est classe hérité\composée d'une TStrings
      Where.Add(CST_BDD_INT_NTCLE, Id, ddbftId, rbEquals); // C'est mieux non ? une seule ligne pour les 2 champs, le type variant et l'opérateur de comparaison
      Where.Add(CST_BDD_INT_DATEMAJ, dMaj, ddbftDateTime, rbGreaterThan); // Where est une collection de WhereItem
      Open(); // Génère le SQL et ouvre le DataSet !
    end;
    Dans mon code mes appels direct à Select furent remplacé par BeginSelect qui renvoyait un TSelectAssistant
    car j'avais tellement de paramètres sinon j'aurais fini avec une douzaine de paramètres dont au moins 6 tableau et 2 sef of !

    Tu notes l'avantage de regrouper ensemble chaque élément, tu as nettement moins de risque d'ajouter un élement à un des tableaux et de l'oublier dans un autre !

    D'ailleurs, pour faciliter le lien entre les tableaux, tu pourrais commencer par écrire comme ceci

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    qq.OpenParameteredRequest(CST_BDD_INTERV, CST_ALLFIELDS,
      [CST_BDD_INT_NTCLE, CST_BDD_INT_DATEMAJ],
      [Id               , dMaj],
      [ddbftId          , ddbftDateTime],
      [rbEquals         , rbGreaterThan],
      );
    Je sais que cela peut en faire hurler plus d'un mais c'est plus lisible, par contre, bcp de Code Formatter dégage ces alignements si utile ! dommage !

    Pour tes constantes Cst_ , utilise des MAJUSCULES pour leurs identifiants ! C'est une norme acceptée par le plus grand nombre, en plus, on a même pas besoin de préfixer par Cst_, on sait que c'est une constante rien que par sa Casse !
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo