如何强制按顺序调用几个函数



我有一个类,它有两个函数foo1和foo2,必须按顺序调用:foo1,foo2。

如何强制用户每次调用foo1时都调用foo2?

有可能在编译时检查这个序列吗?

背景是:我正在开发一个基于流的日志系统,就像cout、cerr等等:

trace << "This is a log text followed by a number " << 5 << endl;

我需要的是强制用户每次调用trace时都调用endl<必须先调用em>endl,然后再调用跟踪

为什么?

  • 当用户调用endl时,日志系统必须刷新。必须尽快刷新每条消息
  • 日志也是同步的,以避免消息重叠。因此,我在trace调用中锁定了一个互斥对象,并在endl呼叫中解锁了它

这些是我的局限性:

  • 它必须尽可能简单,就像cout一样
  • 我使用的是Visual Studio 2010,所以我不能使用c++11
  • 我不能使用boost库,因为我在实时环境中工作,我的老板希望避免这种情况
  • 我处于实时环境(RTX)中,我们不想动态分配内存

像这样:

#include <iostream>
struct foo_caller {
    template<class OtherStuff>
    void call_foos(OtherStuff&& other_stuff)
    {
        foo1();
        other_stuff();
        foo2();
    }
private:
    void foo1()
    {
        std::cout << "foo1" << std::endl;
    }
    void foo2()
    {
        std::cout << "foo2" << std::endl;
    }
};
int main()
{
    foo_caller bar;
    bar.call_foos([] {
        std::cout << "here is some other stuff" << std::endl;
    });
    return 0;
}

预期输出:

foo1
here is some other stuff
foo2

我不太喜欢这个问题,因为imho应该通过说明背景来改进它(即你为什么需要这个?)。然而,我受到了道格拉斯O.莫恩斯答案的启发,只是为了OP告诉我这种方法有什么问题,我建议这样做:

class Foo {
    private:
        bool foo1Called;
    public:
        Foo() : foo1Called(false) {}
        void foo1(){
            assert(!foo1Called && "You have to call foo1();foo2();");
            /*...*/
            foo1Called = true;
        }
        void foo2(){
            assert(foo1Called && "You have to call foo1();foo2();");
            /*...*/
            foo1Called = false;
        }
}

这是可能的,但我仍然强烈建议您不要这样做,而是更改接口(正如评论中所建议的:提供一个公共foo,它按正确的顺序调用私有foo1foo2。实际上封装的目的就是这样)。

附言:我刚刚意识到我没有抓住要点,你希望在编译时进行检查。我想使用一些奇怪的技巧是可能的,但问题再次出现,你为什么需要这个?从一开始就提供正确的界面可以很容易地解决您的问题。

如何强制用户每次调用foo2foo1?有可能在编译时检查这个序列吗?

我喜欢你的问题!

经过10分钟左右的思考,我相信答案是否定的。

我曾经宣称软件是"无限"灵活的。我错了。

在这里,您已经要求编译器或代码阅读其他贡献者的想法。我认为这是不可能的。

我认为foo2()可以通过简单地捕获调用时间来确认foo1()之前在最后一个小时间度量(微秒?毫秒?)内被调用过。但这并不能保证(中断处理可能会延迟10毫秒,以太网可能很有挑战性),也不是你所要求的。

期待其他答案!愿他们再次证明我错了。

<小时>

(更新)

也许知道错误发生得晚总比完全不知道要好。

考虑为每个函数添加一个计数器,在每次调用时递增。

也许foo2()可以断言()foo1()被调用的频率不超过一次?

但是,如果允许用户在不调用foo1()的情况下调用foo2(),这会起作用吗?嗯。也许foo2()必须清除两个计数器?

阅读您的编辑后,我建议使用另一种不需要客户端调用endl的解决方案。

而不是调用

trace << stuff << endl;

调用

Trace() << stuff;

不同之处在于,Trace()实际上是一个构造函数调用,它返回一个从std::ostringstream继承的对象,您可以将其转换到该对象中。这个输出缓冲区对象调用endl和/或打印它或析构函数中的任何内容。(请注意,我们不一定要打印调试日志。)

你可能不喜欢括号,但这给了你另一个机会:使用正确的构造函数,你也可以写:

Trace("The %s is %d!", "foobar", foobar);

或者混合它们:

Trace("The %s is 0x%x", some_string, some_value) << " and then " << some;

这实际上是我们用于日志记录的内容。

最新更新