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也许你可以使用一些工具来生成一个静态调用图,并找到从更新到获取的路径。你可以允许一组固定的函数和类来调用你,而不允许所有其他函数和类。朋友声明就是为了这个。不能在允许所有其他设置的同时禁止固定设置。