読者です 読者をやめる 読者になる 読者になる

とくにあぶなくないRiSKのブログ

危ないRiSKのブログだったかもしれない。本当はRiSKだけどググラビリティとか取得できるIDの都合でsscriskも使ったり。

C++でアスペクト指向

メンバを呼ぶ前後で何かをするために。

int main()
{
 aspect<
  std::vector<int>,
  locker
 > vec = new std::vector<int>;

 vec->push_back(3);
}

こういうコードで push_back の前後で自動的に lock/unlock するように。

コード全体

#include<memory>

template<class Base, template <class> class... Aspects>
class aspect
{
 std::unique_ptr<Base> base;
 class proxy : public Aspects<Base>...
 {
  Base* base;
 public:
  proxy(Base* base)
   : Aspects<Base>(base)..., base(base)
  {}
  Base* operator->()
  {
   return base;
  }
  Base* operator->() const
  {
   return base;
  }
 };
public:
 aspect(Base* ptr)
  : base(ptr)
 {}
 proxy operator->()
 {
  return proxy(base.get());
 }
};

#include<iostream>

template<class T>
struct locker
{
 locker(T*){ std::cout << "lock\n"; }
 ~locker(){ std::cout << "unlock\n"; }
};

template<class T>
struct simple_logger
{
 simple_logger(T*){ std::cout << "log start\n"; }
 ~simple_logger(){ std::cout << "long end\n"; }
};

template<class T>
class size_logger
{
 T* p;
public:
 size_logger(T* p)
  : p(p)
 {
  std::cout << "size() == " << p->size() << '\n';
 }
 ~size_logger()
 {
  std::cout << "size() == " << p->size() << '\n';
 }
};

template<class T>
class capacity_logger
{
 T* p;
public:
 capacity_logger(T* p)
  : p(p)
 {
  std::cout << "capacity() == " << p->capacity() << '\n';
 }
 ~capacity_logger()
 {
  std::cout << "capacity() == " << p->capacity() << '\n';
 }
};

#include<vector>

int main()
{
 aspect<
  std::vector<int>,
  locker,
  simple_logger,
  size_logger,
  capacity_logger 
 > vec = new std::vector<int>;

 vec->push_back(3);
 vec->push_back(1);
 vec->push_back(4);
 vec->push_back(1);
 vec->push_back(5);
 vec->pop_back();
 vec->clear();
}

実行結果:

lock
log start
size() == 0
capacity() == 0
capacity() == 1
size() == 1
long end
unlock
lock
log start
size() == 1
capacity() == 1
capacity() == 2
size() == 2
long end
unlock
lock
log start
size() == 2
capacity() == 2
capacity() == 4
size() == 3
long end
unlock
lock
log start
size() == 3
capacity() == 4
capacity() == 4
size() == 4
long end
unlock
lock
log start
size() == 4
capacity() == 4
capacity() == 8
size() == 5
long end
unlock
lock
log start
size() == 5
capacity() == 8
capacity() == 8
size() == 4
long end
unlock
lock
log start
size() == 4
capacity() == 8
capacity() == 8
size() == 0
long end
unlock

参考:
More C++ Idioms/ポインタ参照前後での実行(Execute-Around Pointer) - Wikibooks
C++ Patterns Executing Around Sequences - Kevlin Henney [PDF]