如何让派生类在调用基类之前执行操作?



在此之前:我不是开发人员,我可能听不懂你的一些信息,而且由于英语不是我的母语,我的问题可能很难理解。

考虑到:

class MyVector  
{
std::vector<command> vec;
std::mutex vector_m;
public:
void MVpush_back(command t)
{
this->vector_m.lock();
this->vec.push_back(t);
this->vector_m.unlock();
}
};

command是一个自定义类(它的内容在这里似乎无关紧要;复制构造函数确实存在(。

基本上,由于我有很多可能的编写器和读者,因此我想强制使用mutex来访问vec参数。

由于我只会使用push_back()erase()find()我可以重新定义它们,但我想知道是否有一种方法不必重新定义所有函数。

像这样:

<template> safe(*function name*<template>)
{
this->vector_m.lock();
<template> retval = vec.*function name*<parameter>;
this->vector_m.unlock();
return retval;
}

其中要调用的函数是一种参数...

我认为可以使用std::initializer_list<type>来完成,但类型要求是阻塞的。

有没有办法做这样的事情?

改写问题:有没有办法将带有参数(1(的函数推送为函数(2(的参数并使函数(2(调用函数(1(?

如果你不介意牺牲成员访问运算符(.(的使用,你可以将所有向量操作整齐地包装成可锁定的操作。

class MyVector {
std::vector<command> vec;
std::mutex vector_m;
struct locker {
MyVector& _ref;
locker(MyVector& parent) : _ref(parent) {
_ref.vector_m.lock();
}
~locker() { _ref.vector_m.unlock(); }
std::vector<command>* operator->() && { return &_ref.vec; }
};
public:
locker operator->() { return {*this}; }
};

现在,每次访问基础向量都将在操作期间锁定和解锁向量:

MyVector mv;
mv->push_back(/* ... */);
// This locks the mutex before doing the push back
// And unlocks it immediately after, even in the face of exceptions.

神奇之处在于operator->以传递的方式行事。它应用于自身的返回值,直到返回常规指针,然后像往常一样访问该指针。但沿途的每一个临时都是按照后进先出的顺序创建和销毁的。因此,临时MyVector::locker对象的生存期或多或少只是访问的持续时间。

这是我在评论中提出的建议的一个快速不是特别精彩的版本; 未编译或测试; 只是一些东西,这样你就可以得到这个想法。

template<class T>
class OverkillProtector {
private:
T& d;
std::unique_lock<std::mutex>lock ;
public:
OverkillProtector(T& d_, std::mutex& m_) :
d(d_),
lock(m_)
{}
OverkillProtector(const OverkillProtector&) = delete;
OverkillProtector& operator =(const OverkillProtector&) = delete;
T& getValue() { return d; }
const T& getValue() const { return d; }
};

请注意,在(默认(deturctor中,唯一锁将被销毁,这将释放互斥锁。 请注意,此对象的生存期必须小于互斥锁或要包装的数据的生存期。

你可以做这样的事情:

class MyVector  
{
std::vector<command> vec;
std::mutex vector_m;
public:
template <typename F>
decltype(auto) Do(F&& f)
{
std::unique_lock<std::mutex> lock{vector_m};
return std::forward<F>(f)(vec);
}
};

用法类似于:

MyVector myVector;
command myCommand;
myVector.Do([&](auto& vec) { vec.push_back(myCommand); });

模板方法可能如下所示:

class MyVector
{
std::vector<command> vec;
mutable std::mutex vector_m;
public:
template <typename R, typename ... T, typename ... P>
R safeCall(R (std::vector<command>::*f)(T ...), P&& ... p)
{
std::lock_guard<std::mutex> l(vector_m);
return (vec.*f)(std::forward<P>(p)...);
}
template <typename R, typename ... T, typename ... P>
R safeCall(R (std::vector<command>::*f)(T ...) const, P&&  ... p) const
{
std::lock_guard<std::mutex> l(vector_m);
return (vec.*f)(std::forward<P>(p)...);
}
};
void test()
{
MyVector v;
v.safeCall(&std::vector<int>::push_back, 7);
MyVector const* vv = &v;
int n = vv->safeCall(&std::vector<int>::operator[], 0);
}

好吧,您可以保护重新实现接口的工作,但是用法变得相当丑陋 - 矢量类型的typedef使其尽可能短,但仍然...宏?

#define safe_call(V, R, F, ...) V R safeCall(&std::vector<int>::F, ## __VA_ARGS__)
safe_call(v, ., push_back, 7);
safe_call(vv, ->, operator[], 1);

或者更短一点:

#define safe_call(V, F, ...) V safeCall(&std::vector<int>::F, ## __VA_ARGS__)
safe_call(v., push_back, 7);
safe_call(vv->, operator[], 1);

好吧,我不会进一步评论,自己决定...

最后,您可能会咬紧牙关并真正复制界面,以便以后更方便地使用 - 不过,帮助程序模板可能会简化任务:

class MyVector
{
std::vector<command> vec;
mutable std::mutex vector_m;
template <typename R, typename ... T>
R safeCall(R (std::vector<command>::*f)(T...), T... t)
{
std::lock_guard<std::mutex> l(vector_m);
return (vec.*f)(t...);
}
// const variant, too
public:
// ...
};
void MyVector::push_back(Command t)
{
safeCall(&std::vector<Command>::push_back, t);
}

最新更新