C++ 编译时检查一个函数是否在另一个函数之前调用
C++ Compile time check if a function called before another one
假设我有一个带有两个成员函数的 class。
class Dummy {
public:
void procedure_1();
void procedure_2();
};
在编译时,我想确保 procedure_1
在 procedure_2
之前被调用。执行此操作的正确方法是什么?
也许您可以使用代理来做到这一点-class。这个想法是,procedure_2
不能直接从外部访问(例如通过将其设为私有)。 procedure_1
会 return 某种允许访问 procedure_2
.
的代理
下面的一些代码,虽然我不认为它干净或安全。如果你愿意,你仍然可以破坏系统。
IMO 这样的要求应该在没有明确验证的情况下处理,因为它非常麻烦并且不可能使其绝对安全。
相反,应该很好地记录依赖关系,这在 C++ 中似乎也是惯用的。如果函数使用不当,您会收到一条警告,可能会发生不好的事情,但没有什么能阻止您开枪。
class Dummy {
private:
void procedure_2() { }
class DummyProxy
{
private:
Dummy *parent; // Maybe use something safer here
public:
DummyProxy(Dummy *parent): parent(parent) {}
void procedure_2() { this->parent->procedure_2(); }
};
public:
[[nodiscard]] DummyProxy procedure_1() {
return DummyProxy{this};
}
};
int main()
{
Dummy d;
// d.procedure_2(); error: private within this context
auto proxy = d.procedure_1(); // You need to get the proxy first
proxy.procedure_2(); // Then
// But you can still break the system:
Dummy d2;
decltype(d2.procedure_1()) x(&d2); // only decltype, function is not actually called
d2.procedure_2(); // ooops, procedure_1 wasn't called for d2
}
不是“检查”它,而是不允许它。不要公开允许以任何其他方式调用它的接口。公开一个允许 仅 按指定顺序调用它的接口。例如:
// library.c
class Dummy {
private:
void procedure_1();
void procedure_2();
public:
void call_Dummy_prodedure_1_then_something_then_produre_2(std::function<void()> f){
procedure_1();
f();
procedure_2();
}
};
您还可以让 procedure_2
从析构函数调用,procedure_1
从构造函数调用。
#include <memory>
struct Dummy {
private:
void procedure_1();
void procedure_2();
public:
struct Procedures {
Dummy& d;
Procedures(Dummy& d) : d(d) { d.procedure_1(); }
~Procedures() { d.procedure_2(); }
};
// just a simple example with unique_ptr
std::unique_ptr<Dummy::Procedures> call_Dummy_prodedure_1_then_produre_2(){
return std::make_unique<Dummy::Procedures>(*this);
}
};
int main() {
Dummy d;
auto call = d.call_Dummy_prodedure_1_then_produre_2();
call.reset(); // yay!
}
以上方法可确保在一个翻译单元内对调用进行排序。要在多个源文件之间进行检查,生成最终的可执行文件,然后编写一个工具来检查生成的程序集,如果有两次或更多次调用 call_Dummy_prodedure_1_then_produre_2
函数,该工具将出错。为此,需要额外的工作来确保 call_Dummy_prodedure_1_then_produre_2
不能被编译器优化。
但是您可以创建一个只能由一个翻译单元包含的 header:
// dummy.h
int some_global_variable_with_initialization = 0;
struct Dummy {
....
};
并将上面的接口公开到 Dummy
或仅在该库中添加包装器声明。这样,如果多个源文件包含 dummy.h
,链接器将出错并显示 multiple definitions
错误。
至于检查,你可以制作prodedure_1
和procedure_2
一些宏,这些宏将扩展为编译器无法优化的带有一些标记的东西,比如汇编注释。然后您可以使用自定义工具检查生成的可执行文件,该工具将检查对 prodedure_1
的调用是否在 procedure_2
.
之前
假设我有一个带有两个成员函数的 class。
class Dummy {
public:
void procedure_1();
void procedure_2();
};
在编译时,我想确保 procedure_1
在 procedure_2
之前被调用。执行此操作的正确方法是什么?
也许您可以使用代理来做到这一点-class。这个想法是,procedure_2
不能直接从外部访问(例如通过将其设为私有)。 procedure_1
会 return 某种允许访问 procedure_2
.
下面的一些代码,虽然我不认为它干净或安全。如果你愿意,你仍然可以破坏系统。
IMO 这样的要求应该在没有明确验证的情况下处理,因为它非常麻烦并且不可能使其绝对安全。 相反,应该很好地记录依赖关系,这在 C++ 中似乎也是惯用的。如果函数使用不当,您会收到一条警告,可能会发生不好的事情,但没有什么能阻止您开枪。
class Dummy {
private:
void procedure_2() { }
class DummyProxy
{
private:
Dummy *parent; // Maybe use something safer here
public:
DummyProxy(Dummy *parent): parent(parent) {}
void procedure_2() { this->parent->procedure_2(); }
};
public:
[[nodiscard]] DummyProxy procedure_1() {
return DummyProxy{this};
}
};
int main()
{
Dummy d;
// d.procedure_2(); error: private within this context
auto proxy = d.procedure_1(); // You need to get the proxy first
proxy.procedure_2(); // Then
// But you can still break the system:
Dummy d2;
decltype(d2.procedure_1()) x(&d2); // only decltype, function is not actually called
d2.procedure_2(); // ooops, procedure_1 wasn't called for d2
}
不是“检查”它,而是不允许它。不要公开允许以任何其他方式调用它的接口。公开一个允许 仅 按指定顺序调用它的接口。例如:
// library.c
class Dummy {
private:
void procedure_1();
void procedure_2();
public:
void call_Dummy_prodedure_1_then_something_then_produre_2(std::function<void()> f){
procedure_1();
f();
procedure_2();
}
};
您还可以让 procedure_2
从析构函数调用,procedure_1
从构造函数调用。
#include <memory>
struct Dummy {
private:
void procedure_1();
void procedure_2();
public:
struct Procedures {
Dummy& d;
Procedures(Dummy& d) : d(d) { d.procedure_1(); }
~Procedures() { d.procedure_2(); }
};
// just a simple example with unique_ptr
std::unique_ptr<Dummy::Procedures> call_Dummy_prodedure_1_then_produre_2(){
return std::make_unique<Dummy::Procedures>(*this);
}
};
int main() {
Dummy d;
auto call = d.call_Dummy_prodedure_1_then_produre_2();
call.reset(); // yay!
}
以上方法可确保在一个翻译单元内对调用进行排序。要在多个源文件之间进行检查,生成最终的可执行文件,然后编写一个工具来检查生成的程序集,如果有两次或更多次调用 call_Dummy_prodedure_1_then_produre_2
函数,该工具将出错。为此,需要额外的工作来确保 call_Dummy_prodedure_1_then_produre_2
不能被编译器优化。
但是您可以创建一个只能由一个翻译单元包含的 header:
// dummy.h
int some_global_variable_with_initialization = 0;
struct Dummy {
....
};
并将上面的接口公开到 Dummy
或仅在该库中添加包装器声明。这样,如果多个源文件包含 dummy.h
,链接器将出错并显示 multiple definitions
错误。
至于检查,你可以制作prodedure_1
和procedure_2
一些宏,这些宏将扩展为编译器无法优化的带有一些标记的东西,比如汇编注释。然后您可以使用自定义工具检查生成的可执行文件,该工具将检查对 prodedure_1
的调用是否在 procedure_2
.