2010-02-04 14 views
3

J'espère que quelqu'un peut avoir une idée sur la façon de contrôler/spécifier l'ordre de macro-expansion. Voici le contexte:Existe-t-il un moyen de contrôler l'ordre de macro expansion?


// 32 bit increments, processor has registers for set, clear and invert 
#define CLR_OFF 1 
#define SET_OFF 2 
#define INV_OFF 3 


#define SET(reg,bits) *((volatile unsigned long*)(& reg+SET_OFF)) = bits 
//Now if I use this I can do it quite nicely with 
#define STATUS_LED 0x0040; 
SET(LATB, STATUS_LED); // LATB is port of the LED. 

J'ai en fait dû déplacer le matériel autour d'un peu au I fin donc décidé de regrouper les informations TPVB avec le STATUS_LED comme si ...


#define STATUS_LED_PORT LATB 
#define STATUS_LED_MASK 0x0040; 
#define STATUS_LED STATUS_LED_PORT, STATUS_LED_MASK 

//And I try to use it via 
SET(STATUS_LED); 

Mais hélas, LATB, 0x0040 est passé à l'argument 1 de la macro SET. Lorsqu'ils ne sont pas utilisés comme une macro cette méthode fonctionne correctement:


inline void SET(u32_t *reg, u32_t bits) { ((volatile u32_t *) (((u32_t)reg) + SET_OFF*4)) = bits; } 
//Change the STATUS_LED macro to 
#define STATUS_LED &STATUS_LED_PORT, STATUS_LED_MASK 
SET(STATUS_LED); //Works great! 

Mais malheureusement, mon compilateur ne voit pas la nécessité de la fonction inline et provoque 6 instructions pour régler le registre par opposition à 4, donc pour une utilisation en bits -banging c'est imprévisible.

J'espère que quelqu'un peut connaître d'une façon d'élargir la STATUS_LED macro d'abord, quelque chose comme: SET(##STATUS_LED)

Actuellement ma solution pour passer est d'avoir deux macros SET et SETRM (set registre, masque) mais je me sens comme il devrait y avoir une solution, car le code SET ressemble ...


#define SETRM(reg,bits) ... 
#define SET(args) SETRM(args) //WHY WOULD THIS GET EXPANDED HERE?? 

Enfin, le compilateur de mon processeur ne supporte pas les n-arguments à une macro, je pensais que je pourrais être en mesure de jouer avec ça mais hélas :(.

Merci beaucoup pour votre temps, et j'apprécierais toutes les pensées, je peux aller de l'avant, mais ce serait tellement plus propre si je pouvais simplement utiliser SET partout.

Répondre

4

Remplacement des paramètres dans l'expansion des macros de type fonction se produit d'une manière réglée. Tous les arguments qui n'apparaissent pas après l'opérateur # ou l'un des côtés d'un ## sont entièrement développés macro lorsqu'ils sont remplacés, pas avant que la macro de type fonction soit développée.

Cela signifie que pour transformer une seule macro en deux arguments de macro, un cycle de substitution de macro doit avoir lieu avant que la fonction requise, telle que la macro, soit elle-même développée.

Cela signifie que la solution d'une seconde fonction telle qu'une macro qui s'étend à la macro de type fonction souhaitée est la solution la plus simple.

dire étant donné votre définition SET originale

#define SET(reg,bits) *((volatile unsigned long*)(& reg+SET_OFF)) = bits 

et une macro qui se développe à deux arguments potentiels

#define STATUS_LED_PORT LATB 
#define STATUS_LED_MASK 0x0040; 
#define STATUS_LED STATUS_LED_PORT, STATUS_LED_MASK 

Vous devez utiliser une autre macro fonction semblable pour obtenir la substitution que vous avez besoin.

par exemple.

#define SET2(x) SET(x) 

Puis SET2(STATUS_LED) comme suit développe.

SET(LATB , 0x0040;) 

puis

*((volatile unsigned long*)(& LATB + 2)) = 0x0040; 

Ce n'est pas valide car il n'y a pas assez d'arguments à la SET macro; les paramètres sont mis en correspondance avec les arguments avant toute expansion de l'argument. Mon compilateur génère une erreur; le comportement n'est pas défini.

SET(STATUS_LED) 
1

Si le nom de la racine est toujours le même, vous pouvez utiliser:

#define SET_COMPOSITE(root) SET(root##_PORT, root##_MASK)