试图避免某些特定C ++代码的代码重复

时间:2016-01-11 16:39:41

标签: c++ templates c++11

我有一些C ++代码,它包含以下模式的重复:

int Func(Type1 arg1, Type2 arg2)
{
  RAIILock lock(Singleton::Mutex());
  Instance* ptr = GetClassInstance();
  if (ptr) {
    return ptr->Func(arg1, arg2);
  } else {
    return -1;
  }
}

基本上,它试图在锁定下获取有效的类实例指针,并且实质上将来自此普通函数的调用转发到具有相同签名的实例方法。 " Func"名称以及参数的数量和类型各不相同,但其余的调用是相同的。

感觉应该有一些方法可以使用模板来实现这一点而不会产生太多的模板魔法,但我还是无法想出任何东西。

3 个答案:

答案 0 :(得分:4)

那样的东西?

template <class MEM_FUN, class... ARGS>
auto call_func(MEM_FUN&& f, ARGS&&... args)
{
  RAIILock lock(Singleton::Mutex());
  Instance* ptr = GetClassInstance();
  if (ptr) {
    return (ptr->*f)(std::forward<ARGS>(args)...);
  } else {
    return -1;
  }
}

这样打电话:

call_func(&Instance::Func, arg1, arg2, arg3);

答案 1 :(得分:2)

我会从这个原语开始:

template<class F>
std::result_of_t<F(Instance*)> while_locked( F&& f ) {
  RAIILock lock(Singleton::Mutex());
  Instance* ptr = GetClassInstance();
  if (ptr)
    return std::forward<F>(f)(ptr):
  else
    return -1;
}

使用:

int x = while_locked( [&](auto* ptr) {
  ptr->foo( a, b );
} );
基本上,您可以在锁定时执行任何操作,并且可以在执行操作时访问ptr。这允许您将多个方法调用合并为一个原子操作。

这可以扩展为多重锁定,在一组ptr源上对互斥量进行排序,适当地锁定它们,从每个源中获取指针,然后调用你的函数。

编写“调用成员函数”版本也变得很容易,但将原子操作限制为“仅调用某个特定成员函数”似乎有问题。

我承认C ++ 14中的语法比使用auto-lambda的C ++ 11更好。但到目前为止,大多数主要的编译器都实现了自动lambda。

有一种更通用的模式,如下所示:

template<class T>
struct lockable {
  template<class F>
  std::result_of_t<F(T const&)> read( F&& f ) const {
    auto l = lock();
    return std::forward<F>(f)(t);
  }
  template<class F>
  std::result_of_t<F(T&)> write( F&& f ) {
    auto l = lock();
    return std::forward<F>(f)(t);
  }
private:
  using read_lock_t = std::shared_lock<std::shared_mutex>;
  using write_lock_t = std::unique_lock<std::shared_mutex>;

  read_lock_t lock() const { return read_lock_t(m); }
  write_lock_t lock() { return write_lock_t(m); }

  mutable std::shared_mutex m;
  T t;
};

可以使用operator=,copy-ctor,move-ctors,default-ctors和多锁定功能进行扩充。多锁定来自std::lock并创建解锁的可锁定。

  read_lock_t unlocked() const { return {m, std::defer_lock}; }
  write_lock_t unlocked() { return {m, std::defer_lock}; }

  template<size_t...Is, class...Selfs>
  friend auto lock_all( std::index_sequence<Is...>, Selfs&... selfs ) {
    auto r = std::make_tuple( selfs.unlocked() );
    std::lock( std::get<Is>(r)... );
    return r;
  }
  template<class...Selfs>
  friend auto lock_all( Selfs&... selfs ) {
    return lock_all( std::index_sequence_for<Selfs...>{}, selfs... );
  }

public:
  template<class F, class...Selfs>
  friend auto invoke( F&& f, Selfs&...selfs )
  -> std::result_of_t< F( decltype(selfs.t)... ) >
  {
    auto l = lock_all(selfs...);
    return std::forward<F>(f)(selfs.t...);
  }

允许您invoke( lambda, a, b, c )使用适当的锁来锁定a bc,然后调用lambda

这样可以轻松编写operator=,例如:

lockable& operator=( lockable&& o ) {
  if (this = std::addressof(o)) return *this;
  return invoke( [](T& target, T& src){
    return target = std::move(src);
  }, *this, o );
}
lockable& operator=( lockable const& o ) {
  if (this = std::addressof(o)) return *this;
  return invoke( [](T& target, T const& src){
    return target = src;
  }, *this, o );
}

答案 2 :(得分:0)

您可能不会应用太多便利并且有一个允许多个成员函数调用的保护单例:

#include <mutex>
template <typename T>
class Lockable
{
    template <typename> friend class Lock;

    public:
    typedef std::mutex mutex_type;
    typedef std::lock_guard<std::mutex> lock_guard;

    public:
    Lockable()
    {}

    template <typename A>
    Lockable(A&& a)
    : _value(std::forward<A>(a))
    {}

    template <typename A, typename ... Args>
    Lockable(A&& a, Args&& ... args)
    : _value(std::forward<A>(a), std::forward<Args>(args)...)
    {}

    Lockable(const Lockable&) = delete;
    Lockable& operator = (const Lockable&) = delete;

    explicit operator T () const {
        lock_guard lock(_mutex);
        T result = _value;
        return result;
    }

    Lockable& operator = (const T& value) {
        lock_guard lock(_mutex);
        _value = value;
        return *this;
    }

    private:
    mutable mutex_type _mutex;
    T _value;
};

template <typename T>
class Lock
{
    public:
    typedef Lockable<T> lockable_type;
    typedef typename lockable_type::mutex_type mutex_type;
    typedef typename lockable_type::lock_guard lock_guard;

    public:
    Lock(lockable_type& lockable)
    : _lock(lockable._mutex), _ptr(&(lockable._value))
    {}

    Lock(const Lock&) = delete;
    Lock& operator = (const Lock&) = delete;

    operator T& () const { return *_ptr; }
    T& operator *  () const { return *_ptr; }
    T* operator -> () const { return _ptr; }

    private:
    lock_guard _lock;
    T* _ptr;
};

class Singleton
{
    private:
    friend class Lockable<Singleton>;

    Singleton() {};

    public:
    Singleton(const Singleton&) = delete;
    Singleton& operator = (const Singleton&) = delete;

    static Lockable<Singleton>& instance();

    int Func0(int arg) const { return 0; }
    int Func1(int arg) const { return 1; }
    int Func2(int arg) const { return 2; }
};

Lockable<Singleton>& Singleton::instance() {
    static Lockable<Singleton> result;
    return result;
}

#include <iostream>

int main() {
    Lock<Singleton> singleton(Singleton::instance());
    singleton->Func0(0);
    singleton->Func1(1);
    singleton->Func2(2);
    std::cout << "Reached\n";
    // Uncomment to get a deadlock
    // Lock<Singleton> deadlock_singleton(Singleton::instance());
    // std::cout << "Not reached\n";
    return 0;
}

注意:Lock<Singleton> singleton(Singleton::instance());由于不可移动std::lock_guard而笨拙。