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

时间:2014-01-03 03:55:54

标签: 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 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);
};

我想要实现的目标:如果在fetch期间调用update,我希望能够编译失败。实际上,我想基于呼叫站点静态禁用该功能。像,

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的实现。

应用程序在许多线程中循环,并为validate的许多实例并行调用T。然后取出全局锁定,并调用update。因此,update的速度至关重要。一般的态度是在validate期间花费您所需的时间,但update应尽可能精益。

我的问题是使用CacheCache基本上是来自SQL的数据对象的内存缓存。

策略是永远不要在Cache::fetch期间调用update,因为在持有锁时可能会进行SQL往返。我们都在努力在团队中培养这种心态。不幸的是,其中一些仍然潜入,并通过代码审查。我们只在系统负载很重的时候注意到它们,一切都停止了。

我想开发一个安全网,防止这种事情被允许。我想要实现的是,如果从Cache::fetch调用T::update,则编译失败。

我不介意它是否可以解决。重点是将其作为障碍;你犯错误的唯一方法就是故意这样做。

<小时/>

我到目前为止

我已经做了一点点,虽然不是我真正追求的。例如,我不希望每次调用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!
    }
};

2 个答案:

答案 0 :(得分:1)

我不知道编译时错误的产生,但可以通过仅对基类进行更新来生成运行时错误。

执行此操作的方法是通过基类中的非虚拟代理函数调用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();在所有实现中,可以高效自动化)。 然后,您可以将一些自动化测试作为构建环境的一部分进行集成,以捕获断言。

答案 1 :(得分:0)

这只是一个愚蠢的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;
}