[Résolu] Portée des variables globales d'une bibliothèque

Bonjour,

Soit le programme test.ino suivant:

#include <Servo.h>

uint16_t ServoCount = 0;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  Serial.println(ServoCount);
}

Mon compilateur me dit qu'il y a une double définition de ServoCount:

libraries\Servo\avr\Servo.cpp.o (symbol from plugin): In function `ServoCount':

(.text+0x0): multiple definition of `ServoCount'

sketch\test.ino.cpp.o (symbol from plugin):(.text+0x0): first defined here

D'ailleurs si j'enlève ma défnition de ServoCount, le compilateur me met en erreur pour le println en me disant que la variable n'est pas définie.

Bien entendu, j'ai bien lu Servo.h et la variable ServoCount n'y est pas. En cherchant bien, je la trouve dans un fichier Servo.cpp

Quelle est la portée de ServoCount qui est dans Servo.h?

Bien entendu c'est un exemple minimum, mais dans la pratique j'ai une bibliothèque avec pleins de variables et j'ai des ennuis si je prends un nom dans mon programme qui est déjà utilisé dans l bibliothèque. Peut-on mettre des variables globales dans une bibliothèque? Faut il systématiquement déclarer une variable globale avec un nom tordu ou encadré par des "_" pour que l'utilisateur puisse prendre les noms courant qu'il veut?

Merci.

Voir

uint8_t ServoCount = 0;                                     // the total number of attached servos

Si la librairie était correctement faite, l'auteur aurait utilisé :

static uint8_t ServoCount = 0;                                     // the total number of attached servos

Sa portée aurait été limitée à Servo.cpp, donc tu n'aurais pas ce problème.

1 Like

Mais si la portée de la variable de Servo.cpp allait jusqu'à loop, en faisant:

#include <Servo.h>

//uint16_t ServoCount = 0;

void setup()
{
  Serial.begin(115200);
}

void loop()
{
  Serial.println(ServoCount);
}

Il devrait m'afficher une valeur et pas me dire

I:\test\test.ino: In function 'void loop()':

test:12:18: error: 'ServoCount' was not declared in this scope

   Serial.println(ServoCount);

                  ^~~~~~~~~~

L'erreur apparait au link, pas à la compilation.
Si tu ne déclares pas ton ServoCount local, l'erreur de compil est normale, car ServoCount n'est défini ni dans ton .ino, ni dans Servo.h
Il est défini dans un des .o de la bibliothèque.
Un erreur apparait au link s'il figure également dans le .o de ton . ino

Effectivement, j'ai mis compile pour link! Mais pourquoi apparaît il encore dans le .o de la bibliothèque, il devrait être local à cette dernière et invisible de l'extérieur? Un peu comme quand on définit une variable à l'intérieur d'un bloc ou d'une fonction. Quelle est la portée d'une telle variable?

Non, c'est bien une erreur de compilation.

Elle est de toutes façons globale, mais elle n'apparaît pas dans Servo.h, probablement parce que l'auteur n'a pas jugé utile de le faire. Si tu veux t'en servir :

extern uint8_t ServoCount;

Ok, je crois que j'ai compris:
Une variable globale déclarée static dans mon cpp n'est visible que dans le cpp et je peux faire ce que je veux dans l'ino
Une variable globale non déclarée static dans le cpp est visible dans le cpp et dans le ino. Je ne peux donc pas la redéfinir parce qu'il y en aurait deux avec la même portée. Je ne peux pas l'utiliser sans dire qu'elle sera déclaré par extern car la compilation de l'ino ne trouve pas la déclaration.

Du coup, cela veut dire que l'on dispose de tas de variables ou de fonctions non documentées.
Application:

void setup()
{
  Serial.begin(115200);
}

extern volatile unsigned long timer0_millis;
void loop()
{
  Serial.println(millis());
  if (millis() > 10000) // 10s
  {
    noInterrupts();
    timer0_millis = 0;
    interrupts();
  }
}

remet à 0 l'horloge millis()

Merci à tous

Oui, voilà, documentées uniquement dans le code.

je parlais de l'erreur du 1er post " multiple definition of ServoCount'` "

Très intéressant... !

Un peu barbare tout de même :wink:

OK.

Je lis dans le fichier:

volatile unsigned long timer0_overflow_count = 0;
volatile unsigned long timer0_millis = 0;
static unsigned char timer0_fract = 0;

J'en conclus:

volatile unsigned long timer0_overflow_count = 0; --> On peut toucher
volatile unsigned long timer0_millis = 0; --> On peut toucher
static unsigned char timer0_fract = 0; --> Pas touche

Barbare peut être pas, mais dangereux, parce qu'à une mise à jour, rien n'est garanti!

Mais cela permet de lire par exemple que les poids faibles de millis() de façon extrêmement rapide parce qu'on a pas à prendre de précautions pour ne lire qu'un octet.