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

时间:2015-12-02 14:06:07

标签: c++ design-patterns

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

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

是否可以在编译时检查此序列?

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

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

我需要的是每次调用 trace 时强制用户调用 endl 。必须在另一次调用 trace 之前调用 endl

为什么?

  • 当用户调用 endl 时,日志系统必须刷新。每条消息都必须尽快刷新。
  • 还会同步日志以避免重叠消息。所以我在 trace 调用中锁定了一个互斥锁,然后在 endl 调用中将其解锁。

这些是我的限制:

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

4 个答案:

答案 0 :(得分:1)

像这样:

#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

答案 1 :(得分:0)

  

如何强制用户每次调用时调用foo2   foo1?是否可以在编译时检查此序列?

我喜欢你的问题!

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

我曾经宣称软件具有'无限'的灵活性。我错了。

在这里,您已经要求编译器或代码读取另一个贡献者的想法。我认为无法做到。

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

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

(更新)

或许知道错误发生的时间晚于完全不了解。

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

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

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

答案 2 :(得分:0)

我不太喜欢这个问题,因为imho应该通过说明背景是什么来改进(即你为什么需要这个?)。然而,我得到了DOUGLAS O. MOENs的回答,并且只是为了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来调用私人foo1和{{1}按照正确的顺序。实际上,封装的目的只是这个。)

PS:我刚刚意识到我错过了这一点,你想在编译时进行检查。我猜这可能是通过使用一些奇怪的技巧,但然后问题再次出现,为什么你需要这个?从一开始就提供正确的界面可以轻松解决您的问题。

答案 3 :(得分:0)

阅读完修改后,我会建议另一种不需要客户致电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;

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