Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Comment remplacer une macro par un équivalent plus C++

12 réponses
Avatar
kangs
Bonjour,

Pour simplifier du code j'utilise des macros, en C++ ce n'est par le
must
parait il.

Voici mon cas :

class A
{
public:
int indicateur1() const;
int indicateur2() const;
double indicateur3() const;
string indicateur4() const;

// ...
};

class FromBDD
: public A
{
public:
// ...
};

La class A contient environ une dizaine de variables, les donn=E9es de A
sont
calcul=E9es et perdues en fin de programme.

La class FromBDD h=E9rite de A et m=E9morise les indicateurs dans une BDD.

J'ai besoins de comparer chaque variable de A avec FromBDD et
d'indiquer les
variables qui ont chang=E9es visuellement.

Actuellement je fais en gros :
#define CMP( indicateur )
if { instA.indicateur() !=3D fromBDD.indicateur() )
{
do_1( irow, icol );
}

for ( ... )
{
CMP( indicateur1 );
CMP( indicateur2 );
...
}

Est ce qu'il existe un moyen permettant de ce passer de la macro CMP
sans
avoir =E0 copier/coller le code ?

Merci.

10 réponses

1 2
Avatar
pjb
kangs writes:

Bonjour,

Pour simplifier du code j'utilise des macros, en C++ ce n'est par le
must
parait il.

Voici mon cas :

class A
{
public:
int indicateur1() const;
int indicateur2() const;
double indicateur3() const;
string indicateur4() const;

// ...
};

class FromBDD
: public A
{
public:
// ...
};

La class A contient environ une dizaine de variables, les données de A
sont
calculées et perdues en fin de programme.

La class FromBDD hérite de A et mémorise les indicateurs dans une BDD.

J'ai besoins de comparer chaque variable de A avec FromBDD et
d'indiquer les
variables qui ont changées visuellement.

Actuellement je fais en gros :
#define CMP( indicateur )



do{
if { instA.indicateur() != fromBDD.indicateur() )



{



do_1( irow, icol );



}



}while(0)


for ( ... )
{
CMP( indicateur1 );
CMP( indicateur2 );
...
}

Est ce qu'il existe un moyen permettant de ce passer de la macro CMP
sans
avoir à copier/coller le code ?



Personnellement, je ne trouve rien à redire sur l'usage d'une macro ici.


Ceci dit, on peut effectivement utiliser une fonction template:

template <class OBJECT,class GETTER> CMP(OBJECT& instance,OBJECT& fromBDD,GETTER getter,int irow,int icol){
if(instance->*getter()!=fromBDD->*getter()){
do_1(irow,icol);
}
}

for(...){
CMP(instA,fromBDD,&A::indicateur1,irow,icol);
CMP(instA,fromBDD,&A::indicateur2,irow,icol);
...
}



--
__Pascal Bourguignon__
Avatar
Olivier Miakinen
Le 17/09/2009 13:27, Pascal J. Bourguignon répondait à kangs :

Actuellement je fais en gros :
#define CMP( indicateur )



do{
if { instA.indicateur() != fromBDD.indicateur() )



{



do_1( irow, icol );



}



}while(0)



Oui aux « » de fin de ligne pour dire que la macro n'est pas finie, en
revanche le « do { ... } while(0) » ne me semble pas indispensable pour
une macro aussi simple et définie dans le même fichier. Ok, cela fait
des « ; » inutiles, mais est-ce que cela gênera le compilateur ?

À la limite, on pourrait même envisager d'écrire :
#define CMP(indicateur)
if (instA.indicateur() != fromBDD.indicateur())
do_1(irow, icol)

Note : il y avait une accolade ouvrante à la place d'une parenthèse
ouvrante, je suppose que c'était une erreur de recopie ici, erreur
absente du fichier source.

Personnellement, je ne trouve rien à redire sur l'usage d'une macro ici.



Moi non plus. Mon avis serait différent si la macro était dans un
fichier .h inclus, au lieu d'être définie dans le fichier même où
elle est utilisée.
Avatar
pjb
Olivier Miakinen <om+ writes:

Oui aux « » de fin de ligne pour dire que la macro n'est pas finie, en
revanche le « do { ... } while(0) » ne me semble pas indispensable pour
une macro aussi simple et définie dans le même fichier. Ok, cela fait
des « ; » inutiles, mais est-ce que cela gênera le compilateur ?



D'accord. Je trouve juste que c'est une bonne habitude à prendre de
toujours mettre les macros "procedures" entre do{ et }while(0).

--
__Pascal Bourguignon__
Avatar
kangs
On 17 sep, 13:27, (Pascal J. Bourguignon)
wrote:
[...]
Ceci dit, on peut effectivement utiliser une fonction template:

template <class OBJECT,class GETTER> CMP(OBJECT& instance,OBJECT& fromBDD ,GETTER getter,int irow,int icol){
    if(instance->*getter()!=fromBDD->*getter()){
        do_1(irow,icol);
    }

}

for(...){
  CMP(instA,fromBDD,&A::indicateur1,irow,icol);
  CMP(instA,fromBDD,&A::indicateur2,irow,icol);
  ...

}



Merci pour la solution, mais je n'arrive pas à la mettre en oeuvre,
j'ai tenter de le faire sur un cas simple :

#include <iostream>

class A
{
public:
A( int i1, double i2 )
: ind1_( i1 )
,ind2_( i2 )
{}

int ind1() const { return ind1_; }
double ind2() const { return ind2_; }

private:
int ind1_;
double ind2_;
};

class B
: public A
{
public:
B( int i1, double i2 )
: A( i1, i2 )
{}
};

template< typename GETTER >
bool cmp( A const& a
,B const& b
,GETTER getter
)
{
if( a->*getter() != b->*getter() )
{
return true;
}
else
{
return false;
}
}

int main()
{
A a( 5, 12 );
B b( 5, 20 );

if ( cmp( a, b, &A::ind1 ) )
std::cout << "ind1 ==n";
else
std::cout << "ind1 !=n";

if ( cmp( a, b, &A::ind2 ) )
std::cout << "ind2 ==n";
else
std::cout << "ind2 !=n";

return 0;
}

Le compilateur me sort :
est.cpp: In function ‘bool cmp(const A&, const B&, GETTER) [with
GETTER = int (A::*)()const]’:
test.cpp:49: instantiated from here
test.cpp:34: erreur: must use ‘.*’ or ‘->*’ to call pointer-to-memb er
function in ‘getter (...)’
test.cpp:34: erreur: must use ‘.*’ or ‘->*’ to call pointer-to-memb er
function in ‘getter (...)’
test.cpp: In function ‘bool cmp(const A&, const B&, GETTER) [with
GETTER = double (A::*)()const]’:
test.cpp:54: instantiated from here
test.cpp:34: erreur: must use ‘.*’ or ‘->*’ to call pointer-to-memb er
function in ‘getter (...)’
test.cpp:34: erreur: must use ‘.*’ or ‘->*’ to call pointer-to-memb er
function in ‘getter (...)’

J'ai essayé de remplacer a->*getter() != b->*getter() par a.*getter() !
= b.*getter() mais ça ne change rien.

Qu'est ce qui cloche ?
Avatar
pjb
kangs writes:

Le compilateur me sort :
est.cpp: In function ‘bool cmp(const A&, const B&, GETTER) [with
GETTER = int (A::*)()const]’:
test.cpp:49: instantiated from here
test.cpp:34: erreur: must use ‘.*’ or ‘->*†™ to call pointer-to-member
function in ‘getter (...)’

Qu'est ce qui cloche ?



Il faut des parentheses: if( (a->*getter)() != (b->*getter)() )

--
__Pascal Bourguignon__
Avatar
pjb
kangs writes:

Le compilateur me sort :
est.cpp: In function ‘bool cmp(const A&, const B&, GETTER) [with
GETTER = int (A::*)()const]’:
test.cpp:49: instantiated from here
test.cpp:34: erreur: must use ‘.*’ or ‘->*†™ to call pointer-to-member
function in ‘getter (...)’

J'ai essayé de remplacer a->*getter() != b->*getter() par a.*gette r() !
= b.*getter() mais ça ne change rien.

Qu'est ce qui cloche ?



Il faut mettre des parentheses autour de (obj->*method) ou
(obj.*method), et puisqu'ici on n'a pas un pointeur mais une réfé rence
vers ces objets, il faut utiliser .*:

if( (a.*getter)() != (b.*getter)() )

SRC="/tmp/c.c++" ; EXE="c" ; g++ -g3 -ggdb3 -o ${EXE} ${SRC} && ./${E XE} && echo status = $?
ind1 !=
ind2 ==
status = 0

--
__Pascal Bourguignon__
Avatar
kangs
On 18 sep, 10:51, (Pascal J. Bourguignon)
wrote:
[...]
Il faut mettre des parentheses autour de (obj->*method) ou
(obj.*method), et puisqu'ici on n'a pas un pointeur mais une référenc e
vers ces objets, il faut utiliser .*:

        if( (a.*getter)() != (b.*getter)() )

SRC="/tmp/c.c++" ; EXE="c" ; g++  -g3 -ggdb3 -o ${EXE} ${SRC} &&  ./${EXE} && echo status = $?
ind1 !=
ind2 ==
status = 0



Merci.
Avatar
kangs
On 17 sep, 16:57, Olivier Miakinen <om+ wrote:
Le 17/09/2009 13:27, Pascal J. Bourguignon répondait à kangs :


[...]
Note : il y avait une accolade ouvrante à la place d'une parenthèse
ouvrante, je suppose que c'était une erreur de recopie ici, erreur
absente du fichier source.


Je n'ai pas copié j'ai juste voulu montrer l'idée...

> Personnellement, je ne trouve rien à redire sur l'usage d'une macro i ci.


Oui surtout que la solution template est plus /lourde/.

Moi non plus. Mon avis serait différent si la macro était dans un
fichier .h inclus, au lieu d'être définie dans le fichier même où
elle est utilisée.


En générale j'évite les macros, quand j'en utilise elles sont locales
à une fonction.
J'ai vraiment du mal a penser qu'un jour les macros n'existeront plus
en C++, je les trouve parfois bien pratiques.
Avatar
pjb
kangs writes:

En générale j'évite les macros, quand j'en utilise elles sont locales
à une fonction.
J'ai vraiment du mal a penser qu'un jour les macros n'existeront plus
en C++, je les trouve parfois bien pratiques.



Si jamais ça arrive, tu pourras toujours passer à Lisp. ;-)



En passant, faire une macro "locale" avec cpp, est assez délicat; Le
mieux que l'on puisse faire, c'est utiliser #undef:

void MaClasse::maMethode(Other* other){
#undef MA_MACRO
#define MA_MACRO(X) (m##X=compute##X(#X,other->get_##X()))
MA_MACRO(nom);
MA_MACRO(age);
MA_MACRO(salaire);
#undef MA_MACRO
}


En Lisp, en plus des macros globales introduites par DEFMACRO, on a
des macros locales:

(defmethod ma-methode ((self ma-classe) other)
(macrolet ((ma-macro (field)
(setf (,field self) (,(intern (format nil "COMPUTE-~A" field))
,(string field)
(,(intern (format nil "GET-~A" field)) other)))))
(ma-macro nom)
(ma-macro age)
(ma-macro salaire)))



--
__Pascal Bourguignon__
Avatar
pjb
(Pascal J. Bourguignon) writes:
En passant, faire une macro "locale" avec cpp, est assez délicat; Le
mieux que l'on puisse faire, c'est utiliser #undef:

void MaClasse::maMethode(Other* other){
#undef MA_MACRO
#define MA_MACRO(X) (m##X=compute##X(#X,other->get_##X()))
MA_MACRO(nom);
MA_MACRO(age);
MA_MACRO(salaire);
#undef MA_MACRO
}


En Lisp, en plus des macros globales introduites par DEFMACRO, on a
des macros locales:

(defmethod ma-methode ((self ma-classe) other)
(macrolet ((ma-macro (field)
(setf (,field self) (,(intern (format nil "COMPUTE-~A" field))


`
Il manque une backquote devant (setf ; désolé pour la confusion.

--
__Pascal Bourguignon__
1 2