0%

Aspect oriented programming in C++11

Aspect oriented programming的技術基礎可以看這裡
我想這篇已經寫得很清楚了,有興趣的話繼續Google。
接著試著用C++來實做一些Complie time的AOP方式。

Decorator Pattern

可以試著在關新的函數前面,加上Before跟After。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
template <typename WrappedType>
class BaseAspect
{
protected:
WrappedType* m_wrappedPtr;

struct AfterWrapper
{
BaseAspect* m_derived;
AfterWrapper(BaseAspect* derived) : m_derived(derived) {};
void operator()(WrappedType* p)
{
m_derived->After(p);
}
};
public:
explicit BaseAspect(WrappedType* p) : m_wrappedPtr(p) {};

virtual void Before(WrappedType* p) {
// Default does nothing
};

virtual void After(WrappedType* p) {
// Default does nothing
}

std::shared_ptr<WrappedType> operator->()
{
Before(m_wrappedPtr);
return std::shared_ptr<WrappedType>(m_wrappedPtr, AfterWrapper(this));
}
};
template <template <typename> class Aspect, typename WrappedType>
Aspect<WrappedType> MakeAspect(WrappedType* p)
{
return Aspect<WrappedType>(p);
}

這邊的重點是在After的實現,因為我們不能像Before的方式直接使用。所以需要使用Metadata存起來。當shared_ptr的scope結束之後,會呼叫自定義的Destructor functor,也就是上面的AfterWrapper。進行後面的After工作。
這邊有個個範例程式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
template <typename WrappedType>
class LoggingAspect : public BaseAspect <WrappedType>
{
public:
LoggingAspect(WrappedType* p) : BaseAspect<WrappedType>(p) {}

void Before(WrappedType* p)
{
std::cout << "entering" << std::endl;
}

void After(WrappedType* p)
{
std::cout << "exiting" << std::endl;
}
};

class X
{
public:
void funcX()
{
std::cout << "it is a test" << std::endl;
}
};

std::shared_ptr<X> p(new X());
MakeAspect<LoggingAspect>(p.get())->funcX();

當然,你也可以用[CRTP](Curiously recurring template pattern)來取代原先的Dynamic Binding,不過CRTP我實在不常用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
template <typename WrappedType, typename DerivedAspect>
class BaseAspect
{
protected:
WrappedType* m_wrappedPtr;

DerivedAspect* GetDerived()
{
return static_cast<DerivedAspect*>(this);
}
struct AfterWrapper
{
DerivedAspect* m_derived;
AfterWrapper(DerivedAspect* derived) : m_derived(derived) {};
void operator()(WrappedType* p)
{
m_derived->After(p);
}
};
public:
explicit BaseAspect(WrappedType* p) : m_wrappedPtr(p) {};

void Before(WrappedType* p) {
// Default does nothing
};

void After(WrappedType* p) {
// Default does nothing
}

std::shared_ptr<WrappedType> operator->()
{
GetDerived()->Before(m_wrappedPtr);
return std::shared_ptr<WrappedType>(m_wrappedPtr, AfterWrapper(GetDerived()));
}
};

template <typename WrappedType>
class LoggingAspect : public BaseAspect <WrappedType, LoggingAspect<WrappedType>>
{
typedef BaseAspect <WrappedType, LoggingAspect<WrappedType>> BaseAspect;
public:
LoggingAspect(WrappedType* p) : BaseAspect(p) {}

void Before(WrappedType* p)
{
std::cout << "entering" << std::endl;
}

void After(WrappedType* p)
{
std::cout << "exiting" << std::endl;
}

};

Function / Bind / Variadic template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
struct Aspect
{
template<typename Func>
Aspect(const Func& f) : m_func(f)
{

}

template<typename T>
void Invoke(T&& value)
{
value.Before();
m_func();
value.After();
}

template<typename Head, typename... Tail>
void Invoke(Head&& head, Tail&&... tail)
{
head.Before();
Invoke(std::forward<Tail>(tail)...);
head.After();
}

private:
std::function<void()> m_func;
};

template<typename... AP>
void Invoke(const std::function<void()>& f)
{
Aspect msp(f);
msp.Invoke(AP()...);
}

這個版本的本質在於m_func,利用Variadic template將m_func的前後包裝起來。達到串連的效果。
不過這個版本也有他的問題,例如不能將物件傳入函數裡面(因為已經變成一個function)。