macro: Vartype sur une cellule dans une fonction

Bonjour,

C'est dans Calc lors de l'utilisation d'une macro libreofficeBasic. Dans mes
fonctions excel VBA, j'utilisais vartype pour connaitre le type du contenu
d'une cellule du classeur. Mais avec libreoffice, j'obtiens un comportement
étrange:
- si j'utilise la fonction depuis une "Sub", ca me renvoie toujours 2,
c'est-à-dire un type integer quelque soit le contenu de la cellule testée.
- si j'utilise ma fonction testvartype dans une cellule excel:
=testvartype(A1) alors ca me renvoie 9, c'est-à-dire un type objet alors que
je souhaite savoir si le contenu est de type string, integer, single,
double, etc.

Remarque 1: ca ne renvoie pas la même chose suivant comment la fonction est
utilisée.
Remarque 2: dans les 2 cas, ca ne fonctionne pas comme je le souhaite dans
les 2 cas (et ca ne correspond pas au comportement d'Excel).

Je voudrais savoir si quelque chose est incorrect dans ce que j'ai fait, si
c'est un bug et surtout comment contourner ou résoudre le problème.

voici mon code:
Option VBASupport 1

sub main()
dim cell as Range
dim typeCell as integer

Set cell = Worksheets("Feuille1").Cells(1, 1)

typeCell = Testvartype(typeCell)
msgbox(cell & " : " & typeCell)

end sub

function Testvartype(parametre as variant) as integer
dim test as integer

test = vartype(parametre)

Testvartype = test
end function

Je vous remercie d'avance et j'espère que j'ai posté au bon endroit (1er
post)!

Bonjour

Je ne pratique plus le VBA Excel depuis longtemps mais je vois au moins deux
erreurs dans ton code :

Après l'instruction suivante...

Set cell = Worksheets("Feuille1").Cells(1, 1)

...la variable cell (définie comme range) contient l'objet représentant la
cellule A1 de la feuille Feuille1

Si tu "passais" cette variable à ta procédure cette dernière renverrait 9
(objet)

Dans ta procédure Main tu ne passes ni cell ni cell.value mais :

typeCell = Testvartype(typeCell)

La variable typeCell mise entre parenthèses n'est pas initialisée et donc...
J'ajoute qu'elle n'est pas nécessaire puisque tu peux passer directement
cell.value

Ci-dessous un exemple de correction.

sub main()
dim cell as Range
dim typeCell as integer

Set cell = Worksheets("Feuille1").Cells(1, 1)

'typeCell = Testvartype(typeCell)
typeCell = Testvartype(Cell.value)
msgbox(cell & " : " & typeCell)

end sub

Cordialement
Pierre-Yves

Oui désolé, j'ai écrit un exemple et je suis allé un peu trop vite.
A la place de "typeCell = Testvartype(typeCell) ", je voulais marquer
"typeCell = Testvartype(Cell) ".

En effet, ca renvoyait 9 dans ce cas.

Ta correction m'apporte une solution partielle en ajoutant le ".value" à
Cell avant de le passer en argument à ma fonction.
Cependant, je n'avais pas expliqué tout mon problème. Je vais expliquer mon
cas plus en détail (je m'y prend peut-être mal).

La fonction FonctionExemple peut etre appelée soit depuis une procédure
MainExemple soit directement depuis une formule dans le classeur. Or dans le
cas où je l'appelle depuis une formule dans le classeur, l'argument Entree1
de la fonction FonctionExemple est une "cell" donc si je teste avec vartype,
celui-ci me renvoie 9. Ceci ne permet pas de déterminer si je peux effectuer
ma fonction correctement ou si je dois renvoyer "erreur type incorrect". Ta
solution en ajoutant ".value" va me permettre de vérifier un peu plus en
testant l'objet dans le cas où vartype me renvoie 9. Cependant l'argument
Entree1 n'est pas forcément de type objet et le code refuse de s'exécuter si
j'écris "Entree1.value" quelque part dans ma fonction (même si à l'intérieur
d'un test qui ne devrait s'exécuter que lorsqu'on est en présence d'un
objet). L'erreur est "Erreur d'execution BASIC '91' variable d'objet non
définie".
De plus tous les objets n'ont pas forcément une propriété value.

Merci de vous etre penché sur mon problème! Si vous voyez des pistes à
creuser ou même une solution, ca serait super!

PS: garder à l'idée que c'est un exemple.
PS2: je pourrais faire 2 fonctions différentes mais je trouve ca dommage car
j'ai des centaines de fonctions qui sont faites ainsi et dupliquer autant de
code sera lourd à gérer pour la maintenance future. Et j'aimerai si possible
trouver une solution qui marche avec Excel et libreoffice (dans Excel, je
n'ai pas ce problème car il doit convertir implicitement cell en
cell.value).

Voici mon code:

Option VBASupport 1
Option Explicit

sub MainExemple()
dim MonParametreEntree as variant
dim test as variant

MonParametreEntree = "exemple" 'test pour simuler avec une
chaine
test = FonctionExemple(MonParametreEntree)
msgbox(MonParametreEntree & " : " & test)

MonParametreEntree = 1 'test pour simuler avec
un entier
test = FonctionExemple(MonParametreEntree)
msgbox(MonParametreEntree & " : " & test)

set MonParametreEntree = Worksheets("Feuille1").Cells(1, 1) 'test pour
simuler une formule dans excel
test = FonctionExemple(MonParametreEntree)
msgbox(MonParametreEntree & " : " & test)

end sub

function FonctionExemple(Entree1 as variant) as variant
dim result as variant
dim msgErreur as string

msgErreur = "" 'initialisation de
msgErreur à une chaine vide

if vartype(Entree1) = 9 then 'test si Entree1 est un
objet
   if vartype(Entree1.value) <> 8 then 'test si Entree1 est
different d'une chaine
      msgErreur = "erreur type incorrect"
   endif
elseif vartype(Entree1) <> 8 then 'test si Entree1 est
different d'une chaine
   msgErreur = "erreur type incorrect"
endif

if msgErreur = "" then 'on calcule seulement
si aucune erreur
   result = len(Entree1) 'permet de renvoyer le
nombre de caractère de la chaine
endif

FonctionExemple = result
end function

Bonjour

dbott wrote

Oui désolé, j'ai écrit un exemple et je suis allé un peu trop vite.

Pas grave... :slight_smile:

dbott wrote

Cependant, je n'avais pas expliqué tout mon problème.

La question est en effet différente... Point n'est besoin de dupliquer, tu
devrais pouvoir utiliser quelque chose comme ceci :

Option VBASupport 1
Option Explicit

sub MainExemple()
dim MonParametreEntree as variant
dim test as variant

MonParametreEntree = "exemple" 'test pour simuler avec une
chaine
test = FonctionExemplePys(MonParametreEntree)
msgbox(MonParametreEntree & " : " & test)

MonParametreEntree = 1 'test pour simuler avec
un entier
test = FonctionExemplePys(MonParametreEntree)
msgbox(MonParametreEntree & " : " & test)

set MonParametreEntree = Worksheets("Feuille1").Cells(1, 1) 'test pour
simuler une formule dans excel

test = FonctionExemplePys(MonParametreEntree)
msgbox(MonParametreEntree & " : " & test)

end sub

function FonctionExemplePys(Entree1 as variant) as variant
dim PysObj as object

select case vartype(Entree1)
  case 0, 1
    FonctionExemplePys = "Empty/Null"
  case 2, 3
    FonctionExemplePys = "Entier"
  case 4, 5, 6
    FonctionExemplePys = "Décimal"
  case 7
    FonctionExemplePys = "Date"
  case 8
    FonctionExemplePys = "Texte"
  case 9
    PysObj = Entree1
    FonctionExemplePys = FonctionExemplePys(PysObj.value)
  case else
    FonctionExemplePys = "Autre"
end select
end function

Nota:

1. L'utilisation du case me semble plus concise dans ces cas de figure...
2. La propriété value permet dans ce cas de figure de retourner le type
correct même dans le cas de chaîne, date...

Cordialement
Pierre-Yves

Bonsoir,

C'est parfait, ca fonctionne exactement comme je voulais! Je suis beaucoup
plus serein maintenant :slight_smile:
Tu as raison pour le select case aussi, c'est plus clair comme tu as
présenté.

Merci!

Il y a encore un détail, si envoie un objet qui n'a pas de propriété value
(par exemple un tableau) à la fonction FonctionExemplePys, ca renvoie
l'erreur "la variable d'objet non définie" sur la ligne utilisant le
PysObj.value. Est-ce que tu connais une méthode pour tester si l'objet a une
propriété (value dans notre cas) avec de l'utiliser? Si non alors je créerai
un autre fil de discussion pour ne pas mélanger (même si c'est un peu lié du
fait de l'utilisation de la propriété value).

Bonsoir

dbott wrote

Est-ce que tu connais une méthode pour tester si l'objet a une propriété
(value dans notre cas) avec de l'utiliser?

Désolé pour le délai... avec le "dynamisme" de la liste je n'ai pas vu tout
de suite ce complément de demande...

Tout d'abord une réponse de principe. On peut bien sûr tenter de tout
prévoir dans une seule procédure mais il faut aussi essayer, AMHA, de ne pas
faire une procédure "usine à gaz" qui pourrait se révéler plus difficile à
mettre au point ou maintenir que des "composants" dédiés à une fonction
spécifique.

Ceci étant ta remarque est légitime puisque je suppose que tu envisages
d'utiliser la fonction dans le tableau et de pouvoir lui passer soit une
cellule, soit une plage.

Pour différencier une plage d'une feuille tu peux utiliser la propriété
ImplementationName : "ScVbaRange" ou "SvVbaRange" (selon que la plage est
passée depuis la procédure Main ou via l'appel depuis le tableau),
ScVbaWorksheet s'il l'objet est une feuille.

Dans le cas d'une plage, il faut encore traiter le cas particulier de la
plage d'une seule cellule (seul cas où la propriété Value est disponible et
a du sens). Tu peux utiliser pour cela isArray(PysObj.value) qui va
indiquer si la propriété contient un tableau (plage) ou non (cellule).

Puisque tu souhaites gérer ces cas de figure différents, j'ai modifié la
procédure Main qui tentait d'afficher la valeur de l'objet à côté du type.
Ceci n'est bien sûr pas possible pour les plages ou une feuille...

Pour terminer, il semble que les fonctions de ce type ne soient pas
recalculées à l'ouverture dans LibO. Encore une fois je ne programme plus en
VBA depuis des années et je passe peut-être à côté de quelque chose.

Application.Volatile, en principe prévu pour cela semble n'être pas pris en
compte. J'ai donc ajouté une procédure forçant le recalcul via
Application.Calculate, déclenchée à l'ouverture du classeur.

http://nabble.documentfoundation.org/file/n3751117/VBATestVarType.ods
VBATestVarType.ods

PS : j'ajoute aussi qu'il serait beaucoup plus simple en LibOBasic "pur" de
tester ces différents cas via les services supportés

Cordialement
Pierre-Yves

Bonsoir!

pierre-yves samyn wrote

Tout d'abord une réponse de principe. On peut bien sûr tenter de tout
prévoir dans une seule procédure mais il faut aussi essayer, AMHA, de ne
pas faire une procédure "usine à gaz" qui pourrait se révéler plus
difficile à mettre au point ou maintenir que des "composants" dédiés à une
fonction spécifique.

Oui c'est vrai qu'il faut faire attention à ne pas produire des usines à gaz
:D. Mon coté rigoureux me pousse à bien vérifier si les hypothèses d'entrée
dans la fonction sont celles attendues ou non et de ne faire le traitement
que dans le premier cas. Ces vérifications de type d'objet étaient donc
essentiels de mon point de vue. Et pour les test de "résistance" des
fonctions, j'ai des outils pour envoyer un peu n'importe quoi en paramètre
et je teste si elles résistent bien, donc je déteste que ca échoue sur mes
vérifications préliminaires au traitement réel de la macro :smiley:

pierre-yves samyn wrote

Pour différencier une plage d'une feuille tu peux utiliser la propriété
ImplementationName : "ScVbaRange" ou "SvVbaRange" (selon que la plage est
passée depuis la procédure Main ou via l'appel depuis le tableau),
ScVbaWorksheet s'il l'objet est une feuille.

Ah super oui, je ne connaissais pas ca. C'est parfait avec ca!

pierre-yves samyn wrote

Pour terminer, il semble que les fonctions de ce type ne soient pas
recalculées à l'ouverture dans LibO. Encore une fois je ne programme plus
en VBA depuis des années et je passe peut-être à côté de quelque chose.

Et là tu me devances même, j'avais prévu de poser cette question dans un
autre fil de discussion! Merci encore!

pierre-yves samyn wrote

PS : j'ajoute aussi qu'il serait beaucoup plus simple en LibOBasic "pur"
de tester ces différents cas via les services supportés

Je suppose aussi, j'y viendrai surement petit à petit :slight_smile:

Merci bien
Cordialement
Damien

Doit-on ajouter résolu au titre du fil de discussion? Je ne vois personne le
faire et en tant que nouveau, j'ai pas envie de faire de betises :smiley:

Bonjour,

Doit-on ajouter résolu au titre du fil de discussion? Je ne vois personne le
faire et en tant que nouveau, j'ai pas envie de faire de betises :smiley:

Sur une liste de discussion c'est moins utile que sur un forum car tu ne
peux pas modifier le message initial.

Bonne journée
JBF