No haría esto personalmente, pero solo se me ocurren nombres únicos. Pero si quieres hacerlo, una forma es utilizar una combinación de if
y for
:
#define FOR_BLOCK(DECL) if(bool _c_ = false) ; else for(DECL;!_c_;_c_=true)
Usted puede usarlo como
FOR_BLOCK(GlTranslate t(1.0, 0.0, 0.0)) {
FOR_BLOCK(GlTranslate t(1.0, 1.0, 0.0)) {
...
}
}
Cada uno de esos nombres se encuentran en ámbitos separados y ganaron' t conflicto. Los nombres internos ocultan los nombres externos. Las expresiones en los bucles if
y for
son constantes y el compilador debe optimizarlas fácilmente.
Si realmente desea pasar una expresión, puede utilizar el truco ScopedGuard (ver Most Important const
), pero necesitará algo más de trabajo para escribirlo. Pero el lado bueno es que podemos deshacernos del bucle for
, y dejar que nuestro objeto evaluar a 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
A continuación, proporciona las adecuadas enter
y leave
funciones:
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;
};
Ahora puede escribir por completo sin un nombre en el lado del usuario:
FOR_BLOCK(GlTranslate(1.0, 0.0, 0.0)) {
FOR_BLOCK(GlTranslate(1.0, 1.0, 0.0)) {
...
}
}
Si desea pasar varias expresiones a la vez, es un poco más complicado, pero puede escribir una plantilla de expresión que actúe en operator,
para recopilar todas las expresiones 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
Necesitas heredar el objeto de RAII scoped_obj<Class>
como lo muestra el siguiente
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;
}
}
Todas ellas implican ninguna de las funciones virtuales, y las funciones involucradas son transparentes para el compilador. De hecho, con lo anterior GLTranslate
cambiar para añadir un único entero a una variable global y al salir de restarlo de nuevo, y el siguiente define GLTranslateE
, hice una prueba:
// 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;
}
De hecho, el CCG a nivel de optimización -O2
salidas esto:
main:
sub $29, $29, 8
ldw $2, $0, j
add $2, $2, 5
stw $2, $0, j
.L1:
add $29, $29, 8
jr $31
¡No me esperaba eso, lo optimicé bastante bien!
no veo por qué pensar en un nombre único es más difícil que la realización de algún conjunto de la llamada macro. –
Por lo que vale, intenté un esquema similar alguna vez. Descubrí que era más fácil crear alguna clase de 'Transformation' que tuviera' push/pop' como tú, con funciones de miembros que hacen llamadas para traducir, etc. Entonces solo tienes una clase, y tú solo estás empujando cuando lo necesites. – GManNickG
Creo que la respuesta es __LINE__ o __COUNTER__ :-) – anon