Je ne voudrais pas le faire personnellement, mais juste de venir avec des noms uniques. Mais si vous voulez le faire, une façon est d'utiliser une combinaison de if
et for
:
#define FOR_BLOCK(DECL) if(bool _c_ = false) ; else for(DECL;!_c_;_c_=true)
Vous pouvez l'utiliser comme
FOR_BLOCK(GlTranslate t(1.0, 0.0, 0.0)) {
FOR_BLOCK(GlTranslate t(1.0, 1.0, 0.0)) {
...
}
}
Chacun de ces noms sont dans des domaines distincts et ne pas entrer en conflit. L'intérieur des noms de masquer l'extérieur noms. Les expressions dans l' if
et for
boucles sont constants et doit être facilement optimisé par le compilateur.
Si vous avez vraiment envie de passer à une expression, vous pouvez utiliser le ScopedGuard truc (voir Plus Important const
), mais il aura besoin d'un peu plus de travail pour l'écrire. Mais le côté sympa, c'est, que nous pouvons nous débarrasser de l' for
boucle, et laissez notre objet d'évaluer à l' false
:
struct sbase {
operator bool() const { return false; }
};
template<typename T>
struct scont : sbase {
scont(T const& t):t(t), dismiss() {
t.enter();
}
scont(scont const&o):t(o.t), dismiss() {
o.dismiss = true;
}
~scont() { if(!dismiss) t.leave(); }
T t;
mutable bool dismiss;
};
template<typename T>
scont<T> make_scont(T const&t) { return scont<T>(t); }
#define FOR_BLOCK(E) if(sbase const& _b_ = make_scont(E)) ; else
Ensuite, vous fournissez le bon enter
et leave
fonctions de:
struct GlTranslate {
GLTranslate(float x, float y, float z)
:x(x),y(y),z(z) { }
void enter() const {
glPushMatrix();
glTranslatef(x, y, z);
}
void leave() const {
glPopMatrix();
}
float x, y, z;
};
Maintenant, vous pouvez l'écrire entièrement, sans un nom du côté de l'utilisateur:
FOR_BLOCK(GlTranslate(1.0, 0.0, 0.0)) {
FOR_BLOCK(GlTranslate(1.0, 1.0, 0.0)) {
...
}
}
Si vous souhaitez passer plusieurs expressions à la fois, c'est un peu plus délicat, mais vous pouvez écrire une expression modèle qui agit sur operator,
pour recueillir toutes les expressions en scont
.
template<typename Derived>
struct scoped_obj {
void enter() const { }
void leave() const { }
Derived const& get_obj() const {
return static_cast<Derived const&>(*this);
}
};
template<typename L, typename R> struct collect
: scoped_obj< collect<L, R> > {
L l;
R r;
collect(L const& l, R const& r)
:l(l), r(r) { }
void enter() const { l.enter(); r.enter(); }
void leave() const { r.leave(); l.leave(); }
};
template<typename D1, typename D2>
collect<D1, D2> operator,(scoped_obj<D1> const& l, scoped_obj<D2> const& r) {
return collect<D1, D2>(l.get_obj(), r.get_obj());
}
#define FOR_BLOCK(E) if(sbase const& _b_ = make_scont((E))) ; else
Vous avez besoin d'hériter de la RAII objet d' scoped_obj<Class>
comme le montre le schéma suivant
struct GLTranslate : scoped_obj<GLTranslate> {
GLTranslate(float x, float y, float z)
:x(x),y(y),z(z) { }
void enter() const {
std::cout << "entering ("
<< x << " " << y << " " << z << ")"
<< std::endl;
}
void leave() const {
std::cout << "leaving ("
<< x << " " << y << " " << z << ")"
<< std::endl;
}
float x, y, z;
};
int main() {
// if more than one element is passed, wrap them in parentheses
FOR_BLOCK((GLTranslate(10, 20, 30), GLTranslate(40, 50, 60))) {
std::cout << "in block..." << std::endl;
}
}
L'ensemble de ces impliquent pas de fonctions virtuelles, et les fonctions concernées sont transparentes pour le compilateur. En fait, avec ce qui précède GLTranslate
modifié pour ajouter un entier à une variable globale, et avant de quitter le soustraire à nouveau, et le ci-dessous définies GLTranslateE
, j'ai fait un test:
// we will change this and see how the compiler reacts.
int j = 0;
// only add, don't subtract again
struct GLTranslateE : scoped_obj< GLTranslateE > {
GLTranslateE(int x):x(x) { }
void enter() const {
j += x;
}
int x;
};
int main() {
FOR_BLOCK((GLTranslate(10), GLTranslateE(5))) {
/* empty */
}
return j;
}
En fait, la GCC au niveau d'optimisation en -O2
sorties ceci:
main:
sub $29, $29, 8
ldw $2, $0, j
add $2, $2, 5
stw $2, $0, j
.L1:
add $29, $29, 8
jr $31
Je n'aurais pas cru qu'il optimisé tout à fait bien!