AOP是近年炒得很熱,但卻用得很少的一門技術,不過這并不能阻止我去學習它。既然能一度炒得火熱,必定有過人之處。說AOP是一種思想或許更適合一些,它并不描述哪一種專有的技術,也不指定實現方式。
眾所周知,C++沒有豐富的動態類型信息,更沒有動態生成類的功能(C++類型在編譯后就基本上沒有類型存在了),所以無法像java一樣采用動態代理來實現AOP。
Aspect C++是C++的一個AOP實現,它使用了插入代碼的方法。
一個典型的Aspect C++示例需要一個C++源文件(.cpp)、一個Aspect C++源文件(.ah),通過ac++編譯器把C++源文件和Aspect C++源文件轉換成混合的C++源文件(如果有頭文件也會轉換),最后通過普通的C++編譯器編譯出可執行文件。
下面是一個簡單的示例:
1、C++源文件:
#include <stdio.h>
class A {
public:
int a(int i, float b);
};
int A::a(int i, float b) {
printf("inside A::a(%d, %f)\n", i, b);
return i;
}
void b(char c, char *str) {
printf("inside b(%c, %s)\n", c, str);
}
int main() {
A a;
a.a(4711, 3.14);
b('H', "ello World");
return 0;
}
2、Aspect C++源文件:
#include <stdio.h>
aspect Action {
advice execution("% A::%(
)") || execution("% b(
)") : around() {
printf("A: before(exec) %s\n", JoinPoint::signature());
printf("that : %p\n" , tjp->that());
printf("target: %p\n" ,tjp->target());
tjp->proceed();
printf("A: after(exec) %s\n", JoinPoint::signature());
}
advice call("% A::%(
)") || call("% b(
)") : around() {
printf("A: before(call) %s\n", JoinPoint::signature());
printf("that : %p\n" , tjp->that());
printf("target: %p\n" ,tjp->target());
tjp->proceed();
printf("A: after(call) %s\n", JoinPoint::signature());
}
};
aspect ActionB {
advice execution("% A::%(
)") || execution("% b(
)") : around() {
printf("B: before(exec) %s\n", JoinPoint::signature());
printf("that : %p\n" , tjp->that());
printf("target: %p\n" ,tjp->target());
tjp->proceed();
printf("B: after(exec) %s\n", JoinPoint::signature());
}
advice call("% A::%(
)") || call("% b(
)") : around() {
printf("B: before(call) %s\n", JoinPoint::signature());
printf("that : %p\n" , tjp->that());
printf("target: %p\n" ,tjp->target());
tjp->proceed();
printf("B: after(call) %s\n", JoinPoint::signature());
}
};
簡單說明一下:
1、“aspect Action”定義了一個“方面”,名字是“Action”,定義一個方面可以理解為“我關注程序的這個方面”。
2、“advice 切入點:位置”定義一個“處理方法”,在切入點的指定位置上執行代碼。切入點可以選擇call、execution、construction、destruction,分別表示調用、執行、構造函數、析構函數。執行點可以選擇before、after、around,分別表示在這些切入點的前面、后面或替換掉整個函數。
3、tpj表示thisJoinPoint,表示切入點本身。上面的例子由于使用around替換了整個執行過程,所以要執行原來的操作還需要調用tpj->proceed()。這里的around完成的功能可由一個before和一個after代替
4、切入點的匹配模式。切入點通過字符串來匹配要切入的操作,“%”字符表示匹配任意類型(或名字),在AspectJ中,這個字符是“*”,由于C++中“*”用來定義指針,所以在Aspect C++中用“%”;“...”用來匹配任意個參數。
編譯:
首先運行ac++ -p <你的源文件所在目錄> -d <輸出文件目錄> -I<附加頭文件目錄>,這一步會轉換C++源文件和Aspect C++源文件。
如果在安裝了VC,編譯時可指定INCLUDE路徑及_WIN32宏。
ac++ -p <你的源文件所在目錄> -d <輸出文件目錄> -I<附加頭文件目錄> -I"C:\Program Files\Microsoft Visual Studio 8\VC\include" -D_WIN32
然后直接編譯:
cl <源文件名>.cc
上面這個程序在處理前運行結果如下:
F:\soft\ac\examples\Action>main
inside A::a(4711, 3.140000)
inside b(H, ello World)
經Aspect C++處理后運行結果:
F:\soft\ac\examples\Action-out>main
A: before(call) int A::a(int,float)
that : 00000000
target: 0012FF73
B: before(call) int A::a(int,float)
that : 00000000
target: 0012FF73
A: before(exec) int A::a(int,float)
that : 0012FF73
target: 0012FF73
B: before(exec) int A::a(int,float)
that : 0012FF73
target: 0012FF73
inside A::a(4711, 3.140000)
B: after(exec) int A::a(int,float)
A: after(exec) int A::a(int,float)
B: after(call) int A::a(int,float)
A: after(call) int A::a(int,float)
A: before(call) void b(char,char *)
that : 00000000
target: 00000000
B: before(call) void b(char,char *)
that : 00000000
target: 00000000
A: before(exec) void b(char,char *)
that : 00000000
target: 00000000
B: before(exec) void b(char,char *)
that : 00000000
target: 00000000
inside b(H, ello World)
B: after(exec) void b(char,char *)
A: after(exec) void b(char,char *)
B: after(call) void b(char,char *)
A: after(call) void b(char,char *)