C++ 我可以静态阻止一个函数调用另一个函数吗?
我有以下接口:C++ 我可以静态阻止一个函数调用另一个函数吗?,c++,C++,我有以下接口: class T { public: // Called in parallel virtual unsigned validate () = 0; // Called with a lock taken out virtual unsigned update () = 0; }; template <typename DataType> class Cache { public: // Gets the requested
class T {
public:
// Called in parallel
virtual unsigned validate () = 0;
// Called with a lock taken out
virtual unsigned update () = 0;
};
template <typename DataType>
class Cache {
public:
// Gets the requested object.
// If it doesn't exist in memory, go to SQL.
unsigned fetch (DataType &data);
// Gets the requested object.
// If it's not in memory, returns NOT_FOUND.
unsigned find (DataType &data);
};
用法如下所示:
class A_T : public T {
public:
virtual unsigned validate () {
global_cache.fetch (object); // OK
}
virtual unsigned update () {
global_cache.find (object); // Also OK
global_cache.fetch (object); // FAIL!
}
};
class A_T : public T {
public:
virtual unsigned validate () {
global_cache.fetch (object, CACHE_KEY); // OK
}
virtual unsigned update () {
global_cache.fetch (object, CACHE_KEY); // Can't do it!
}
};
背景 在我的项目中大约有500个
T
的实现
应用程序在多个线程中循环,并并行调用T
的多个实例的validate
。然后取出一个全局锁,并调用update
。因此,更新的速度至关重要。一般的态度是在验证
过程中花费您需要的任何时间,但是更新
应该尽可能精简
我的问题是使用缓存
。缓存基本上是SQL中数据对象的内存缓存
策略是在update
期间永远不要调用Cache::fetch
,因为持有锁时可能会发生SQL往返。我们都在努力在团队中培养这种心态。不幸的是,其中一些仍然潜入,并通过了代码审查。我们只在系统负载过重,一切都停止时才注意到它们
我想建立一个安全网,防止这种事情被允许。如果从T::update
调用Cache::fetch
,我希望实现的是编译失败
我不介意它是否可以解决。关键是要把它作为一个障碍;你犯错误的唯一方法就是故意犯错误
到目前为止我得到了什么
我已经在这方面取得了一些进展,尽管还不是我真正想要的。例如,我不希望每次调用都更改为fetch
template <typename Impl>
class cache_key {
cache_key() { }
friend unsigned Impl::validate();
};
#define CACHE_KEY cache_key<std::remove_pointer<decltype(this)>::type> ()
T
的实现可能如下所示:
class A_T : public T {
public:
virtual unsigned validate () {
global_cache.fetch (object); // OK
}
virtual unsigned update () {
global_cache.find (object); // Also OK
global_cache.fetch (object); // FAIL!
}
};
class A_T : public T {
public:
virtual unsigned validate () {
global_cache.fetch (object, CACHE_KEY); // OK
}
virtual unsigned update () {
global_cache.fetch (object, CACHE_KEY); // Can't do it!
}
};
我不知道编译时错误的生成,但可以通过只更新基类来生成运行时错误
一种方法是通过基类中的非虚拟代理函数调用update,该函数将基类的状态设置为检测我们正在更新,因此不应调用fetch
class updateWatcher()
{
public:
updateWatcher(bool *valIn) : val(valIn) {*val=true;}
~updateWatcher() {*val=false;}
private:
bool* val;
}
class T {
public:
// Called in parallel
virtual unsigned validate () = 0;
unsigned updateProxy()
{
updateWatcher(&inUpdate); //exception safe tracker we are in update
return update();
}
void
protected:
// Called with a lock taken out
virtual unsigned update () = 0;
bool inUpdate; // tells if we are in update or not
};
class A_T : public T {
public:
virtual unsigned validate () {
global_cache.fetch (object,inUpdate); // OK
}
virtual unsigned update () {
global_cache.find (object); // Also OK
global_cache.fetch (object,inUpdate); // FAIL (put assert in global_cache.fetch) !
}
};
这不会产生编译,但会产生运行时错误,好处是不需要更新任何实现(除了将所有全局_cache.fetch(…);替换为全局_cache.fetch(…,inUpdate);并在所有实现中调用update()到updateProxy();这可以高效地自动化)。
然后,您可以将一些自动测试作为构建环境的一部分进行集成,以捕获断言。这只是一个愚蠢的POC,我不建议这样做,并且可能无法满足您的期望:
struct T {
// Called in parallel
virtual unsigned validate () = 0;
// Called with a lock taken out
virtual unsigned update () = 0;
};
struct A_T : T {
unsigned validate () override;
unsigned update () override;
};
template <typename DataType>
class Cache {
private:
class Privileged {
friend class Cache<DataType>;
friend unsigned A_T::validate();
Privileged( Cache<DataType> &outer ) : outer(outer) {}
// Gets the requested object.
// If it doesn't exist in memory, go to SQL.
unsigned fetch (DataType &data);
Cache<DataType> &outer;
};
public:
Privileged privileged { *this };
// Gets the requested object.
// If it's not in memory, returns NOT_FOUND.
unsigned find (DataType &data);
};
Cache<int> global_cache;
unsigned A_T::validate () {
int object;
global_cache.privileged.fetch (object); // OK
return 1;
}
unsigned A_T::update () {
int object;
global_cache.find (object); // Also OK
global_cache.privileged.fetch (object); // FAIL!
return 1;
}
struct{
//并行调用
虚拟无符号验证()=0;
//我打电话来的时候把锁打开了
虚拟未签名更新()=0;
};
结构A\u T:T{
未签名的validate()覆盖;
未签名的更新()覆盖;
};
模板
类缓存{
私人:
阶级特权{
好友类缓存;
朋友未签名的A_T::validate();
特权(缓存和外部):外部(外部){}
//获取请求的对象。
//如果它在内存中不存在,请转到SQL。
未签名的获取(数据类型和数据);
Cache&outer;
};
公众:
特权{*this};
//获取请求的对象。
//如果它不在内存中,则返回not_FOUND。
未签名查找(数据类型和数据);
};
缓存全局缓存;
未签名的A\u T::验证(){
int对象;
全局_cache.privileged.fetch(对象);//确定
返回1;
}
未签名的A\u T::更新(){
int对象;
全局_cache.find(object);//也可以
全局_cache.privileged.fetch(对象);//失败!
返回1;
}
你试过使用std::mutex吗?
第三个想法:制定一项众所周知的政策,任何人如果打了愚蠢的“禁止”电话,都必须单脚站立,发出响亮的咯咯声,夹杂着短语“我在哄你”,在一个非常公开的地方持续15分钟,然后在那里拍摄。编译时间?不,运行时间?对让它什么都不做,记录,崩溃,抛出,断言,格式c:如果你这么做了?当然可以。@anthony arnold也许你可以使用一些工具来生成一个静态调用图,并找到从更新到获取的路径。你可以允许一组固定的函数和类来调用你,而不允许所有其他函数和类。朋友声明就是为了这个。不能在允许所有其他设置的同时禁止固定设置。