c++ - C/C++ 宏/模板 blackmagic 生成唯一名称

标签 c++ c-preprocessor raii

宏很好。 模板很好。 几乎任何工作都很好。

例子是OpenGL;但该技术是特定于 C++ 的,不依赖于 OpenGL 知识。

精确问题:

我想要一个表达式 E;我不必指定唯一名称;这样在定义 E 的地方调用构造函数,在 block E 结束的地方调用析构函数。

例如,考虑:

class GlTranslate {
  GLTranslate(float x, float y, float z); {
    glPushMatrix();
    glTranslatef(x, y, z);
  }
  ~GlTranslate() { glPopMatrix(); }
};

手动解决方案:

{
  GlTranslate foo(1.0, 0.0, 0.0); // I had to give it a name
  .....
} // auto popmatrix

现在,我不仅为 glTranslate 提供了这个,还有很多其他 PushAttrib/PopAttrib 调用。我宁愿不必为每个 var 想出一个唯一的名称。是否有一些涉及宏模板的技巧......或者其他会自动创建一个变量的东西,该变量的构造函数在定义点被调用;并在 block 结束时调用析构函数?

谢谢!

最佳答案

我不会亲自这样做,只是想出独特的名字。但如果你想这样做,一种方法是使用 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循环是恒定的,应该很容易被编译器优化。


如果你真的想传递一个表达式,你可以使用 ScopedGuard 技巧(见 Most Important 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(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

然后您提供正确的 enterleave功能:

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)) {
    ...
  }
}

如果你想一次传递多个表达式,这有点棘手,但你可以编写一个作用于 operator, 的表达式模板将所有表达式收集到 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> 继承 RAII 对象如下图所示

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更改为将单个整数添加到全局变量并在离开时再次减去它,以及下面定义的 GLTranslateE ,我做了一个测试:

// 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;
}

事实上,优化级别的 GCC -O2输出这个:

main:
    sub     $29, $29, 8
    ldw     $2, $0, j
    add     $2, $2, 5
    stw     $2, $0, j
.L1:
    add     $29, $29, 8
    jr      $31

我没想到,它优化得很好!

关于c++ - C/C++ 宏/模板 blackmagic 生成唯一名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2419650/

相关文章:

c++ - 这可能是内存泄漏吗?

c++ - 在 Windows 下为 OpenCV 编译 MinGW 库

c++ - 了解 RAII 对象

c++ - 是否可以毫无异常(exception)地进行 RAII?

c++ - 添加初始化列表的元素

c++ - 函数的模板返回类型编译错误

c - 定义变量与初始化

c - 为什么我的宏不能编译?

c++ - 在 C++ 中使用 "#define"的替代方案?为什么会被人嫌弃?

C++ 在 map 中放置()时防止析构函数调用