我有一个类,它有两个函数foo1和foo2,必须按顺序调用:foo1,foo2。
如何强制用户每次调用foo1时都调用foo2?
有可能在编译时检查这个序列吗?
背景是:我正在开发一个基于流的日志系统,就像cout、cerr等等:
trace << "This is a log text followed by a number " << 5 << endl;
我需要的是强制用户每次调用trace时都调用endl<必须先调用em>endl,然后再调用跟踪。必须先调用em>
为什么?
- 当用户调用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
,它按正确的顺序调用私有foo1
和foo2
。实际上封装的目的就是这样)。
附言:我刚刚意识到我没有抓住要点,你希望在编译时进行检查。我想使用一些奇怪的技巧是可能的,但问题再次出现,你为什么需要这个?从一开始就提供正确的界面可以很容易地解决您的问题。
如何强制用户每次调用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;
这实际上是我们用于日志记录的内容。