函数调用防护

发布于 2024-07-27 23:26:34 字数 353 浏览 10 评论 0 原文

假设我有一个名为 InitFoo 的免费函数。 我想保护这个函数不被意外多次调用。 我没有多想就写下了以下内容:

void InitFoo()
{
    {
        static bool flag = false;
        if(flag) return;
        flag = true;
    }

    //Actual code goes here.
}

不过,这看起来像一个大疣。 InitFoo 不需要保留任何其他状态信息。 有人可以建议一种方法来实现相同的目标而不丑陋吗?

当然,宏不算数。

Suppose I have a free function called InitFoo. I'd like to protect this function from being called multiple times by accident. Without much thought I wrote the following:

void InitFoo()
{
    {
        static bool flag = false;
        if(flag) return;
        flag = true;
    }

    //Actual code goes here.
}

This looks like a big wart, though. InitFoo does not need to preserve any other state information. Can someone suggest a way to accomplish the same goal without the ugliness?

Macros don't count, of course.

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

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

发布评论

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

评论(8

爱你不解释 2024-08-03 23:26:35

您还需要它是多线程安全的吗? 研究带有双重检查锁定的单例模式(这很容易出错)。

如果您不想为此使用整个类,另一种简单的方法是:

在 .cpp 中(不要在 .h 中声明 InitBlah)

 // don't call this -- called by blahInited initialization
static bool InitBlah() 
{
   // init stuff here
   return true;
}
bool blahInited = InitBlah();

没有人可以在此 .cpp 之外调用它,并且它会被调用。 当然,有人可以在这个 .cpp 中调用它——取决于你对它不可能、不方便和有记录的关心程度。

如果您关心顺序或在特定时间执行操作,那么 Singleton 可能适合您。

Do you also need it to be multi-thread safe? Look into the Singleton pattern with double-check locking (which is surprising easy to get wrong).

If you don't want a whole class for this, another simple way is:

In a .cpp (don't declare InitBlah in the .h)

 // don't call this -- called by blahInited initialization
static bool InitBlah() 
{
   // init stuff here
   return true;
}
bool blahInited = InitBlah();

No one can call it outside of this .cpp, and it gets called. Sure, someone could call it in this .cpp -- depends on how much you care that it's impossible vs. inconvenient and documented.

If you care about order or doing it at a specific time, then Singleton is probably for you.

ヤ经典坏疍 2024-08-03 23:26:35

我就是这么做的。 如果您想要替代方案,您可以使用一些函数指针改组:

static void InitFoo_impl()
{
    // Do stuff.

    // Next time InitFoo is called, call abort() instead.
    InitFoo = &abort;
}

void (*InitFoo)() = &InitFoo_impl;

That is exactly how I'd do it. You could use some function pointer shuffling if you want an alternative:

static void InitFoo_impl()
{
    // Do stuff.

    // Next time InitFoo is called, call abort() instead.
    InitFoo = &abort;
}

void (*InitFoo)() = &InitFoo_impl;
醉酒的小男人 2024-08-03 23:26:35

好吧,构造函数只会自动调用一次。 如果您创建此类的单个实例:

class Foo
{
public:
    Foo(void)
    {
        // do stuff
    }
}

那么 //do stuff 将仅执行一次。 执行两次的唯一方法是创建该类的另一个实例。

您可以通过使用 Singleton 来防止这种情况。 实际上,//do stuff 只能被调用一次。

Well, a constructor is only automatically called once. If you create a single instance of this class:

class Foo
{
public:
    Foo(void)
    {
        // do stuff
    }
}

Then //do stuff will only execute once. The only way to execute it twice is to create another instance of the class.

You can prevent this by using a Singleton. In effect, //do stuff can only possibly be called once.

一抹微笑 2024-08-03 23:26:35

我想保护此函数不被意外多次调用

对我来说,这听起来像是一个只会在调试过程中出现的问题。 如果是这样的话,我会简单地执行以下操作:

void InitFoo()
{
    #ifndef NDEBUG
       static bool onlyCalledOnce = TRUE;
       assert(onlyCalledOnce);
       onlyCalledOnce = FALSE;
    #endif

    ...
}

这个特定的疣的目的很容易通过查看它来辨别,并且如果程序员错误地调用 < code>InitFoo 多次。 它也将在生产代码中完全消失。 (当定义NDEBUG时)。

编辑:关于动机的快速说明:
多次调用 init 函数可能是一个很大的错误。 如果此函数的最终用户错误地调用了它两次,那么静静地忽略该错误可能不是正确的方法。 如果您不采用 assert() 路线,我建议至少将消息转储到 stdoutstderr

I'd like to protect this function from being called multiple times by accident

To me, this sounds like an issue that will only come up during debugging. If that is the case, I would simply do the following:

void InitFoo()
{
    #ifndef NDEBUG
       static bool onlyCalledOnce = TRUE;
       assert(onlyCalledOnce);
       onlyCalledOnce = FALSE;
    #endif

    ...
}

The purpose of this particular wart is easily discerned just by looking at it, and it will cause a nice, big, flashy assertion failure if a programmer ever makes the mistake of calling InitFoo more than once. It will also completely dissapear in production code. (when NDEBUG is defined).

edit: A quick note on motivation:
Calling an init function more than once is probably a big error. If the end user of this function has mistakenly called it twice, quietly ignoring that mistake is probably not the way to go. If you do not go the assert() route, I would recommend at least dumping a message out to stdout or stderr.

铁轨上的流浪者 2024-08-03 23:26:35

您可以通过一些不同的丑陋来做到这一点:

struct InitFoo
{
     InitFoo()
     {
         // one-time code goes here
     }
};

void Foo()
{
    static InitFoo i;
}

您仍在使用 static,但现在您不需要进行自己的标志检查 - static 已经放入了一个标志并对其进行检查,因此它仅构造 i 一次。

You can do it with some different ugliness:

struct InitFoo
{
     InitFoo()
     {
         // one-time code goes here
     }
};

void Foo()
{
    static InitFoo i;
}

You're still using static, but now you don't need to do your own flag checking - static already puts in a flag and a check for it, so it only constructs i once.

柒夜笙歌凉 2024-08-03 23:26:35

在需要一次性但不值得整堂课的情况下,我总是这样做。 当然,它假设您不担心与线程相关的问题。 我通常在变量名前加上“s_”前缀,以明确它是一个静态变量。

I do exactly that all the time with situations that need that one-time-only-but-not-worth-making-a-whole-class-for. Of course, it assumes you don't worry about thread-related issues. I usually prefix the variable name with "s_" to make it clear that it's a static variable.

韬韬不绝 2024-08-03 23:26:35

嗯...如果您不反对使用Boost,那么请看看boost::call_once

namespace { boost::once_flag foo_init_flag = BOOST_ONCE_INIT; }

void InitFoo() {
    // do stuff here
}

void FooCaller() {
    boost::call_once(&foo_init_flag, InitFoo);
    // InitFoo has been called exactly once!
}

void AnotherFooCaller() {
    boost::call_once(&foo_init_flag, InitFoo);
    // InitFoo has been called exactly once!
}

Hmmm... if you don't object to using Boost, then have a look at boost::call_once:

namespace { boost::once_flag foo_init_flag = BOOST_ONCE_INIT; }

void InitFoo() {
    // do stuff here
}

void FooCaller() {
    boost::call_once(&foo_init_flag, InitFoo);
    // InitFoo has been called exactly once!
}

void AnotherFooCaller() {
    boost::call_once(&foo_init_flag, InitFoo);
    // InitFoo has been called exactly once!
}
半葬歌 2024-08-03 23:26:35

我并不是对此感到非常兴奋,但这只是另一种方式:函数对象。

#import <iostream>

class CallOnce {
private:
    bool called;
public:
    CallOnce() {
        called = false;
    }
    void operator()(void) {
        if (called) {
            std::cout << "too many times, pal" <<std::endl;
            return;
        }
        std::cout << "I was called!" << std::endl;
        called = true;
    }

};

int main(void) {
    CallOnce call;

    call();
    call();
}

Not that I am very excited about it, but this is just another way: function object.

#import <iostream>

class CallOnce {
private:
    bool called;
public:
    CallOnce() {
        called = false;
    }
    void operator()(void) {
        if (called) {
            std::cout << "too many times, pal" <<std::endl;
            return;
        }
        std::cout << "I was called!" << std::endl;
        called = true;
    }

};

int main(void) {
    CallOnce call;

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