是否可以防止 RAII 样式的类被“匿名”实例化?

发布于 2024-08-05 09:48:20 字数 715 浏览 11 评论 0原文

假设我有一个 RAII 风格的 C++ 类:

class StateSaver
{
  public:
    StateSaver(int i) { saveState(); }
    ~StateSaver() { restoreState(); }
};

...在我的代码中像这样使用:

void Manipulate()
{
  StateSaver save(1);

  // ...do stuff that modifies state
}

...目标是进入某个状态,做一些事情,然后在离开该范围时离开该状态。有没有办法让这个拼写错误不编译(或警告,或以某种方式抱怨,以便可以注意到错误)?

void Manipulate()
{
  StateSaver(1); // ruh-roh, state saved and immediately restored!

  // ...do stuff that modifies state
}

我不知道 C++ 本身有什么可以用来防止这种情况,但这并不意味着它不存在。如果 C++ 中没有任何内容,则特定于编译器的扩展是可以接受的。我主要对任何针对 gcc 和 msvc 的东西感兴趣(有一天 icc,欢迎其他编译器的想法,但不太可能有用),因此对它们中的任何一个的 hack 都会有用(当然,抽象成适当的 #ifdef'd 宏定义) 。

Suppose I have an RAII-style C++ class:

class StateSaver
{
  public:
    StateSaver(int i) { saveState(); }
    ~StateSaver() { restoreState(); }
};

...to be used like so in my code:

void Manipulate()
{
  StateSaver save(1);

  // ...do stuff that modifies state
}

...the goal being to enter some state, do stuff, then leave that state when I leave that scope. Is there a way to make this typo not compile (or warn, or somehow complain so that the mistake can be noticed)?

void Manipulate()
{
  StateSaver(1); // ruh-roh, state saved and immediately restored!

  // ...do stuff that modifies state
}

I'm not aware of anything in C++ itself which I could use to prevent this, but that doesn't mean it doesn't exist. If there isn't anything in C++, compiler-specific extensions would be acceptable. I'm primarily interested in anything targeting gcc and msvc (someday icc, ideas for other compilers welcome but less likely to be useful) so hacks for any of them would be useful (abstracted into appropriately #ifdef'd macro definitions, of course).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梓梦 2024-08-12 09:48:20

我不确定是否可以在编译时完成任何操作。对于运行时检查,您可以这样做:

struct SaveMatrix
{
    SaveMatrix(const SaveMatrix& that) {
        assert(this == &that);
        glPushMatrix();
    }
    ~SaveMatrix() { glPopMatrix(); }
};

这需要客户端编写:

SaveMatrix sm(sm);

如果不将临时变量绑定到标识符,则无法对临时变量执行相同的操作(此时它与自动变量没有什么不同)。

I'm not sure if anything can be done at compile-time. For a run-time check, you could do this:

struct SaveMatrix
{
    SaveMatrix(const SaveMatrix& that) {
        assert(this == &that);
        glPushMatrix();
    }
    ~SaveMatrix() { glPopMatrix(); }
};

Which requires the client to write:

SaveMatrix sm(sm);

and there's no way to do the same for a temporary without binding it to an identifier (at which point it's no different from an auto variable).

仅冇旳回忆 2024-08-12 09:48:20

SaveMatrix save(); 也没有定义对象。它声明了一个函数。

您几乎无法阻止他人(或您自己,FTM)做他们不想做的事情。我唯一能想到的就是不写代码本身,而是写一个宏。

#define SAVE_MATRIX SaveMatrix save ## __LINE__

然而,这是相当难看的。 OTOH,它确实在编译时捕获了错误。

SaveMatrix save(); doesn't define an object either. It declares a function.

There's very little you can do to prevent others (or yourself, FTM) from doing something else than they wanted to. The only thing I can think of is not writing the code itself, but writing a macro instead.

#define SAVE_MATRIX SaveMatrix save ## __LINE__

However, this is quite ugly. OTOH, it does catch the error at compile-time.

暮年 2024-08-12 09:48:20

实际上,我必须从变体 Waldo 中通过多种方式调整我的解决方案发布了,但我最终得到的是一个宏化版本:

class GuardNotifier
{
    bool* notified;
  public:
    GuardNotifier() : notified(NULL) { }
    void init(bool* ptr) { notified = ptr; }
    ~GuardNotifier() { *notified = true; }
};
class GuardNotifyReceiver
{
    bool notified;
  public:
    GuardNotifyReceiver() : notified(false) { }
    void init(const GuardNotifier& notifier)
      { const_cast<GuardNotifier&>(notifier).init(¬ified); }
    ~GuardNotifyReceiver() { assert(notified); }
};
class StateSaver
{
    GuardNotifyReceiver receiver;
  public:
    StateSaver(int i,
               const GuardNotifier& notifier = GuardNotifier())
    {
      receiver.init(notifier)
      saveState();
    }
    ~StateSaver()
    {
      restoreState();
    }
};

I actually had to tweak my solution in a bunch of ways from the variant Waldo posted, but what I eventually got to is a macro-ized version of:

class GuardNotifier
{
    bool* notified;
  public:
    GuardNotifier() : notified(NULL) { }
    void init(bool* ptr) { notified = ptr; }
    ~GuardNotifier() { *notified = true; }
};
class GuardNotifyReceiver
{
    bool notified;
  public:
    GuardNotifyReceiver() : notified(false) { }
    void init(const GuardNotifier& notifier)
      { const_cast<GuardNotifier&>(notifier).init(¬ified); }
    ~GuardNotifyReceiver() { assert(notified); }
};
class StateSaver
{
    GuardNotifyReceiver receiver;
  public:
    StateSaver(int i,
               const GuardNotifier& notifier = GuardNotifier())
    {
      receiver.init(notifier)
      saveState();
    }
    ~StateSaver()
    {
      restoreState();
    }
};
听风念你 2024-08-12 09:48:20

该类永远无法判断它是被实例化为临时变量(SaveMatrix())还是变量(SaveMatrix save;)。我认为阻止程序员在没有堆栈或宏黑客的情况下这样做的最佳方法是在构造后强制调用成员函数,例如:

class RAII
{
public:
    bool valid;
    RAII()
        : valid(false)
    {
        cout << "RAII ctor" << endl;
    }

    void Do()
    {
        valid = true;
    }

    ~RAII()
    {
        assert(valid);
        cout << "RAII dtor" << endl;
    }
};

然后按如下方式工作:

{
    // Intended use
    RAII raii;
    raii.Do();

    cout << "Some task" << endl;
}

{
    // Woops: forgot Do()
    RAII raii;

    cout << "Some task" << endl;
}

{
    // Woops: forgot Do()
    RAII();

    cout << "Some task" << endl;
}

{
    // Programmer shot self in foot, hopefully the act of typing this would make them realise that
    RAII().Do();

    cout << "Some task" << endl;
}

The class can never tell if it was instantiated as a temporary (SaveMatrix()) or as a variable (SaveMatrix save;). I think the best way to stop the programmer doing that without stack or macro hacks is to force a member function call after construction, eg:

class RAII
{
public:
    bool valid;
    RAII()
        : valid(false)
    {
        cout << "RAII ctor" << endl;
    }

    void Do()
    {
        valid = true;
    }

    ~RAII()
    {
        assert(valid);
        cout << "RAII dtor" << endl;
    }
};

This then works as follows:

{
    // Intended use
    RAII raii;
    raii.Do();

    cout << "Some task" << endl;
}

{
    // Woops: forgot Do()
    RAII raii;

    cout << "Some task" << endl;
}

{
    // Woops: forgot Do()
    RAII();

    cout << "Some task" << endl;
}

{
    // Programmer shot self in foot, hopefully the act of typing this would make them realise that
    RAII().Do();

    cout << "Some task" << endl;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文