C 公共元件 輕量級AOP庫

2021-10-03 18:51:20 字數 3627 閱讀 3854

aop(aspect-oriented programming,面向方面程式設計),可以解決物件導向程式設計中的一些問題,是oop的一種有益補充。物件導向程式設計中的繼承是一種從上而下的關係,不適合定義從左到右的橫向關係,如果繼承體系中的很多無關聯的物件都有一些公共行為,這些公共行為可能分散在不同的元件、不同的物件之中,通過繼承方式提取這些公共行為就不太合適了。使用aop還有一種情況是為了提高程式的可維護性,aop將程式的非核心邏輯都「橫切」出來,將非核心邏輯和核心邏輯分離,使我們能集中精力在核心邏輯上,

在上圖中,每個業務流程都有日誌和許可權驗證的功能,還有可能增加新的功能,實際上我們只關心核心邏輯,其他的一些附加邏輯,如日誌和許可權不需要關注,這時,就可以將日誌和許可權等非核心邏輯「橫切」出來,使核心邏輯盡可能保持簡潔和清晰,方便維護。這樣「橫切」的另乙個好處是,這些公共的非核心邏輯被提取到多個切面中了,使它們可以被其他元件或物件復用,消除了重複**。

aop把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的乙個特點是,它們經常發生在核心關注點的多處,而各處都基本相似,比如許可權認證、日誌、事務處理。aop的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。

實現aop的技術分為:靜態織入和動態織入。靜態織入一般採用專門的語法建立「方面」,從而使編譯器可以在編譯期間織入有關「方面」的**,aspectc++就是採用的這種方式。這種方式還需要專門的編譯工具和語法,使用起來比較複雜。10.3節將要介紹的aop框架正是基於動態織入的輕量級aop框架。動態織入一般採用動態**的方式,在執行期對方法進行攔截,將切面動態織入到方法中,可以通過**模式來實現。下面看乙個簡單的例子,使用**模式實現方法的攔截,如下面**所示:

#include#include#includeusing namespace std;

class ihello

virtual ~ihello(){}

virtual void output(const string& str){}

};class hello : public ihello

private:

ihello* m_ptr;

};void testproxy()

int main()

輸出結果如下:

before real output

it is a test

before real output

通過helloproxy**物件實現了對output方法的攔截,這裡hello::output就是核心邏輯,helloproxy實際上就是乙個切面,我們可以把一些非核心邏輯放到裡面,比如在核心邏輯之前的一些校驗,在核心邏輯執行之後的一些日誌等。

雖然通過**模式可以實現aop,但是這種實現還存在一些不足之處:

·不夠靈活,不能自由組合多個切面。**物件是乙個切面,這個切面依賴真實的物件,如果有多個切面,要靈活地組合多個切面就變得很困難。這一點可以通過裝飾模式來改進,雖然可以解決問題但還是顯得「笨重」。

·耦合性較強,每個切面必須從基類繼承,並實現基類的介面。

我們希望能有乙個耦合性低,又能靈活組合各種切面的動態織入的aop框架。

要實現靈活組合各種切面,乙個比較好的方法是將切面作為模板的引數,這個引數是可變的,支援1到n(n>0)切面,先執行核心邏輯之前的切面邏輯,執行完之後再執行核心邏輯,然後執行核心邏輯之後的切面邏輯。這裡,可以通過可變引數模板來支援切面的組合。aop實現的關鍵是動態織入,實現技術就是攔截目標方法,只要攔截了目標方法,我們就可以在目標方法執行前後做一些非核心邏輯,通過繼承方式來實現攔截,需要派生基類並實現基類介面,這使程式的耦合性增加了。為了降低耦合性,這裡通過模板來做約束,即每個切面物件必須有before(args…)或after(args…)方法,用來處理核心邏輯執行前後的非核心邏輯。下面介紹如何實現能靈活組合各種切面的動態織入的aop框架,如下面**所示:

_pragma("once")

#define has_member(member)\

template\

struct has_member_##member\

;\};\

has_member(before)

has_member(after)

class noncopyable;

templatestruct aspect : noncopyable

templatetypename std::enable_if::value&&has_member_after::value>::type invoke(args&&... args, t&& aspect)

templatetypename std::enable_if::value&&!has_member_after::value>::type invoke(args&&... args, t&& aspect)

templatetypename std::enable_if::value&&has_member_after::value>::type invoke(args&&... args, t&& aspect)

templatevoid invoke(args&&... args, head&&headaspect, tail&&... tailaspect)

private:

func m_func; //被織入的函式

};//aop的輔助函式,簡化呼叫

templatevoid invoke(func&&f, args&&... args)

#include #include #include "aspect.hpp"

using namespace std;

struct aa, 1);

invoke({});

}/*------------------------------------*/

struct timeelapsedaspect

void after(int i)

};struct loggingaspect

void after(int i)

};void foo(int a)

int main()

實現思路很簡單,將需要動態織入的函式儲存起來,然後根據引數化的切面來執行before(args…)處理核心邏輯之前的一些非核心邏輯,在核心邏輯執行完之後,再執行after(args…)來處理核心邏輯之後的一些非核心邏輯。上面**中的has_member_before和has_member_after這兩個traits是為了讓使用者用起來更靈活。使用者可以自由選擇before和after,可以僅僅有before或after,也可以二者都有。

需要注意的是切面中的約束,因為通過模板引數化切面,要求切面必須有before或after函式,這兩個函式的入參必須和核心邏輯的函式入參保持一致,如果切面函式和核心邏輯函式入參不一致,則會報編譯錯誤。從另外乙個角度來說,也可以通過這個約束在編譯期就檢查到某個切面是否正確。

從測試結果中看到,我們可以任意組合切面,非常靈活,也不要求切面必須從某個基類派生,只要求切面具有before或after函式即可(這兩個函式的入參要和攔截的目標函式的入參相同)。

(原創) C 輕量級AOP框架

c 中開源的aop框架aspectc 需要單獨編譯才能將切面的 織入到核心邏輯 中,感覺使用起來不方便,不能滿足快速開發要求。我希望只要實現方法攔截即可,能織入before 和after 操作就行,不追求動態織入。思路是這樣的,通過乙個包裝類,裡面定義before 和after 方法,和 運算子過載...

uClibc(輕量級C庫)

參考 http www.uclibc.org gnu的glibc是乙個非常寵大而完整的庫,至少對於嵌入式系統來說,其體積顯得過於大了一些。uclibc的提出較好的解決了這樣乙個問題。uclibc盡可能的相容glibc,大多數應用程式可以在很小或完全不修改的情況下就可能使用uclibc替代glibc。...

LCM 輕量級通訊元件

lcm和zmq比較 基於lcm和zeromq的程序間通訊研究 2 簡介 lcm lightweight commuciation and marshalling 它是作為訊息傳遞和封裝的通訊庫,其首要任務是簡化低時延訊息傳遞系統的開發。目前廣泛應用於無人駕駛汽車領域。其通訊效率比基於tcp的ros庫...