C / C ++macros/模板blackmagic生成唯一的名称
macros是好的。 模板很好。 几乎无论它的工作是好的。
这个例子是OpenGL; 但该技术是C ++特有的,并且不依赖于OpenGL。
精确的问题:
我想要一个expression式E; 我不必指定一个唯一的名字; 这样一个构造函数在E被定义的地方被调用,并且一个析构函数被调用的地方在块E的末尾。
例如,考虑:
class GlTranslate { GLTranslate(float x, float y, float z); { glPushMatrix(); glTranslatef(x, y, z); } ~GlTranslate() { glPopMatrix(); } };
手动解决scheme
{ GlTranslate foo(1.0, 0.0, 0.0); // I had to give it a name ..... } // auto popmatrix
现在,我不仅为glTranslate,而且还有很多其他的PushAttrib / PopAttrib调用。 我宁愿不必为每个变种提出一个独特的名字。 是否有一些涉及macros模板的技巧…或其他什么东西会自动创build一个variables谁的构造函数在定义点被调用; 和析构函数在块结束时调用?
谢谢!
如果你的编译器支持__COUNTER__
(它可能),你可以尝试:
// boiler-plate #define CONCATENATE_DETAIL(x, y) x##y #define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y) #define MAKE_UNIQUE(x) CONCATENATE(x, __COUNTER__) // per-transform type #define GL_TRANSLATE_DETAIL(n, x, y, z) GlTranslate n(x, y, z) #define GL_TRANSLATE(x, y, z) GL_TRANSLATE_DETAIL(MAKE_UNIQUE(_trans_), x, y, z)
对于
{ GL_TRANSLATE(1.0, 0.0, 0.0); // becomes something like: GlTranslate _trans_1(1.0, 0.0, 0.0); } // auto popmatrix
我个人不会这样做,只是想出独一无二的名字。 但是,如果你想这样做,一种方法是使用if
和for
的组合:
#define FOR_BLOCK(DECL) if(bool _c_ = false) ; else for(DECL;!_c_;_c_=true)
你可以使用它
FOR_BLOCK(GlTranslate t(1.0, 0.0, 0.0)) { FOR_BLOCK(GlTranslate t(1.0, 1.0, 0.0)) { ... } }
这些名称中的每一个都在单独的作用域中,不会发生冲突。 内部名称隐藏外部名称。 if
和for
循环中的expression式是常量,应该由编译器轻松优化。
如果你真的想传递一个expression式,你可以使用ScopedGuard技巧(见最重要的const
),但是需要更多的工作来编写它。 但好的一面是,我们可以摆脱for
循环,让我们的对象评估为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(ot), 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
然后您提供正确的enter
和leave
function:
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; };
现在你可以在用户端完全没有名字地写下来:
FOR_BLOCK(GlTranslate(1.0, 0.0, 0.0)) { FOR_BLOCK(GlTranslate(1.0, 1.0, 0.0)) { ... } }
如果你想一次传递多个expression式,那就更加棘手了,但是你可以编写一个expression式模板,作用于operator,
将所有的expression式都收集到一个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
您需要从scoped_obj<Class>
inheritanceRAII对象,如下所示
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; } }
所有这些都不涉及虚函数,所涉及的函数对于编译器是透明的。 事实上,上面的GLTranslate
改变为添加一个整数到一个全局variables,当再次减去它,以及下面定义的GLTranslateE
,我做了一个testing:
// 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; }
事实上,在优化级别-O2
GCC输出:
main: sub $29, $29, 8 ldw $2, $0, j add $2, $2, 5 stw $2, $0, j .L1: add $29, $29, 8 jr $31
我不会期望的,它优化得相当好!
我认为现在可以做这样的事情:
struct GlTranslate { operator()(double x,double y,double z, std::function<void()> f) { glPushMatrix(); glTranslatef(x, y, z); f(); glPopMatrix(); } };
然后在代码中
GlTranslate(x, y, z,[&]() { // your code goes here });
显然,C ++ 11是需要的