我可以静态地阻止一个函数调用另一个函数吗

Can I statically prevent one function from calling another?

本文关键字:一个 函数调用 另一个 函数 静态 我可以      更新时间:2023-10-16

我有以下接口:

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);
};

我想要实现的目标:如果在update期间调用fetch,我希望能够使编译失败。实际上,我想根据调用站点静态禁用该函数。类似

std::enable_if <callsite_is_not_implementation_of_T_update, unsigned> 
fetch (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!
}
};


背景

在我的项目中大约有500个T的实现。

应用程序在许多线程中循环,并并行地为T的许多实例调用validate。然后取出一个全局锁,并调用update。因此,update的速度至关重要。一般的态度是在validate期间花你需要的任何时间,但update应该尽可能瘦。

我的问题是使用CacheCache基本上是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> ()

所以现在Cache::fetch看起来像:

unsigned fetch (DataType &object, const cache_key &key);

T的实现可能如下所示:

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) !
}
};

这不会产生编译而是运行时错误,好处是不需要更新任何实现(除了替换所有global_cache.fetch(…);通过global_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;
}
相关文章: