跳轉至內容

Aros/開發者/文件/庫/OOP

來自Wikibooks,開放世界中的開放書籍
Aros維基書籍的導航欄
Aros使用者
Aros使用者文件
Aros使用者常見問題
Aros使用者應用程式
Aros使用者DOS Shell
Aros/使用者/AmigaLegacy
Aros開發文件
Aros開發者文件
從AmigaOS/SDL移植軟體
Zune初學者指南
Zune .MUI類
SDL初學者指南
Aros開發者構建系統
特定平臺
Aros x86完整系統HCL
Aros x86音訊/影片支援
Aros x86網路支援
Aros Intel AMD x86安裝
Aros儲存支援IDE SATA等
Aros Poseidon USB支援
x86-64支援
摩托羅拉68k Amiga支援
Linux和FreeBSD支援
Windows Mingw和MacOSX支援
Android支援
Arm Raspberry Pi支援
PPC Power Architecture
其他
Aros公共許可證

oop.library允許驅動程式使用OO(面向物件設計)。HIDDs是AROS中OO用法的另一個示例。

僅供討論 - OOP二進位制物件模型?

[編輯 | 編輯原始碼]

二進位制物件模型的論證。能夠從在安裝時靜態編譯的虛擬機器中使用C++和其他程式語言的能力,尤其是在您希望在此過程中放棄JIT的情況下。

此外,我還需要將任何OS特定結構或變數的使用包裝在一個也在安裝時連結的庫中。這可能只是一個託管的AROS。

但Amiga風格的庫在任何有意義的方式上都不是物件。AmigaOS中的面向物件設計並非以庫為中心,而是以其他結構為中心。轉向更OO的方向可能很有趣,但向庫新增介面並不能實現這一點。

為了將OOP作為二進位制物件模型來實現,我獲得了Marius Schwarz的OOP4a原始碼。其中存在一些嚴重的缺陷。它使用線性搜尋來查詢命令的名稱,並且不支援介面繼承。我知道如何在.library中實現Java風格的介面,但是對線性名稱查詢的依賴使得這些介面無法使用。

當我開始製作我的Object.library實現時,我決定介面繼承優於多重繼承,因為它不會產生可怕的菱形依賴關係。我使用雜湊表而不是線性搜尋來進行名稱查詢,並用C而不是68k彙編編寫它。我不太確定AROS如何在沒有FD檔案的情況下處理其庫標頭檔案,但我意識到它們在AROS上必須與處理器無關。

這讓我們回到了主題:二進位制物件模型。OOP4a在68k AmigaOS上的libs:classes/中使用子目錄來指示它附帶的原型類庫的繼承結構層次結構。這將使名稱空間在庫之間保持分離,以便任何物件都不會干擾另一個物件的名稱(在合理的範圍內)。我打算為libs:interfaces/目錄中的每個介面使用一個單獨的介面檔案,該檔案將成為所有介面的全域性儲存庫,除了直接繼承介面(在OS 4中稱為“主”介面)。即使我的最初計劃是使用雜湊表進行名稱查詢,直接繼承介面也可以包含在其定義庫的同一目錄中。

OOP4a中繼承的實現方式是在庫的全域性區域中以A6的正偏移量儲存父連結。這意味著,當開啟類庫時,將訪問其父目錄並開啟父庫,從而啟動遞迴,一直到遇到空值的根類。在我的Object.library實現中,Object.library類無論如何都是根類,從而消除了父類可能沒有名稱查詢等的某些特殊情況。

oop.library正在執行其中一些操作,但我確信這是正確的方法。我確實喜歡Amiga LVO表而不是雜湊表,以避免在載入期間進行名稱繫結。我可能可以使用一個雜湊表用於目錄,然後在每個目錄中使用LVO表。

我認為庫的OS 4介面實際上也正在執行此操作。它允許在一個庫中擁有不同的介面,每個介面都有自己的名稱和LVO表。

但這確實要求開發人員在編譯庫期間告知哪種方法或函式位於LVO表的哪個位置。

重新建立OS 4風格的介面表很容易。讓我煩惱的是,您需要一個指向介面的指標,位於任何暫存器中(最好在PPC和其他暫存器豐富的處理器上為每個介面使用不同的暫存器)。我假設庫的基本指標也將包含在介面結構中。

在聽到反對雜湊表名稱查詢的意見後,我可以說開啟這樣的庫會很慢。同樣,繼承鏈也會更慢,因為它必須對直接繼承的每個階段執行名稱查詢,直到解析為止。這讓我們來到了一個大問題:如果介面不僅僅是一個名稱,我們該如何儲存它們?胖指標?透過對繼承的每個階段使用相同的雜湊值來加快雜湊查詢速度?

框架不應該需要名稱改編方案,而是使用libs:中的子目錄來組織名稱空間依賴關係並支援Java風格的介面繼承。缺點是我計劃使用名稱的雜湊表,而不是像AmigaOS那樣需要FD或SFD檔案來初始化其vtable。

當我檢視OOP.library的原始碼時,我不喜歡它在執行時執行所有操作的方式。我打算製作Object.library來補充OOP.library。您認為這是否合適,或者應該像OS 4那樣直接新增到Exec.library中?我對像OS 4那樣做太多的事情的擔憂是,OS 4有一個實用程式可以從派生自SFD檔案的XML描述符生成C骨架程式碼。這意味著為除C以外的語言製作一個生成庫的編譯器,這使得事情變得非常棘手。

為了允許LLVM為多種AROS風格生成程式碼,我必須用其他程式碼替換或包裝C執行時庫的一些函式,以確保它們不會對變數大小和結構大小做出假設。在GCC中,sizeof和offsetof被渲染為宏以獲得最小的開銷,但這妨礙了可移植性,因此需要用函式來實現它們。

如果我們想更進一步,並使其為所有作業系統生成程式碼,我們將需要不對結構本身做出任何假設,因此FILE *必須在包裝程式碼中的每個例項中替換為void *(或LLVM表示的i8 *;指向位元組的指標),然後僅在目標平臺上的第二次連結時恢復為FILE *。對於嘗試將跨平臺LLVM程式碼中的C stdio.h標頭檔案包裝的早期嘗試,您可以檢視Mattathias團隊在SVN瀏覽器中啟動的專案。

顯然,LVO方法在載入時速度更快,並且載入時連結可能很慢——以OpenOffice等應用程式為例,最佳化載入時連結是加快Linux上應用程式啟動的重要部分。但我不確定在大多數情況下它會有多大影響……

也沒有任何東西可以阻止支援/使用直接偏移量並提供名稱查詢。例如,我正在慢慢地開發一個Ruby編譯器(用Ruby編寫),雖然我儘可能地使用C++風格的vtable,但它也需要支援名稱查詢,因為如果在Ruby中使用某些結構,則無法在編譯時確定將使用的完整方法名稱集,並且您還需要支援“send”,這意味著它需要一個名稱=>方法對映。所以我有了vtable,但會新增一個雜湊表,將名稱對映到vtable槽,用於無法在編譯時確定偏移量的情況。

例如,向庫定義新增一個函式指標,可以在執行時使用它來查詢庫的元資料,例如名稱=> lvo查詢,這將是一個相當非侵入性的更改,只會減慢由於某種原因實際需要基於名稱查詢的應用程式的速度。

至於AmigaOS和AROS中使用的OOP方法,那在另一個層面上慢得驚人……在Amiga硬體上這樣做是有道理的,因為它可以做到非常記憶體高效,但存在可以更快且允許相同擴充套件性的方法……我不確定這是否重要——對於這兩種情況,我們真正應該擁有一些實際的測量和分析資料。

不幸的是,OS4從未引入實際的用例。OS4介面試圖模仿一點的是Windows上已知的COM或Mozilla套件中已知的XPCOM。在那裡,您擁有可以實現多個介面的物件,所有這些介面都繼承自IUnknown。

COM方法QueryInterface在exec中被exec.library/GetInterface替換。與OS4版本相比,COM/XPCOM解決方案可以對我們想要的*任何*介面進行呼叫。如果存在其他程式語言,則COM風格的物件將在OS 4上實現相同型別的目的。不幸的是,沒有。C++類需要接口才能以二進位制庫格式實現自身。

連結包含一些類似C的虛擬碼,演示了介面在介面繼承鏈中的實現和用法。

如果您想要了解低階C程式碼的工作原理,此連結是更高級別的虛擬碼。

目前沒有。據我所知,只有少數系統庫使用了除“主”介面之外的其他任何東西(最著名的例子是expansion.library及其pci介面)。

我建議閱讀這篇以獲取靈感。

協議擴充套件:一種構建大型可擴充套件軟體系統的方法(Michael Franz博士,1994年)。

協議擴充套件使用vtable,但允許它們在執行時透過將更改傳播到繼承鏈中來動態更新。呼叫比Amiga/AROS BOOPSI風格的分派快得多——派生類中最派生的類的vtable始終包含要呼叫的正確方法指標,因此與C++虛擬函式呼叫的成本相比,只是一次額外的間接定址。

缺點是,如果類層次結構很大,則普通的vtable會變得很大,因為每個類的vtable都需要為每種可能的方法分配一個槽。可以透過將API拆分為不繼承自單個根類的較小介面來緩解此問題,實際上建立了一個淺的、稀疏的trie方法指標。

當然,如果您想要基於名稱查詢方法,則仍然存在查詢成本,但對於可以針對包含靜態方法ID的符號表進行編譯的應用程式,您可以獲得非常好的效能,並且具有與基於分派函式的OO系統(如BOOPSI)一樣好的靈活性(因為只要vtable足夠大,您就可以在執行時新增/刪除方法,並以分派程式作為完全動態呼叫的最壞情況的回退)。

(順便說一句,Franz寫了他的博士論文,研究了一種用於體系結構獨立二進位制檔案的方法,稱為語義字典編碼;最近,他聲名鵲起的是跟蹤樹,與Andreas Gal共同開發,用於TraceMonkey和LuaJIT)。

我認為我可能會將子目錄限制為名稱空間解析。除此之外,我之前寫的大部分內容看起來都不錯。我最初計劃擁有LVO以及名稱查詢。這使我不必為介面檔案中的方法名稱建立單獨的檔案。此外,與協議擴充套件技術不同,我將保留一個根類,因為它只定義了一個“toString”方法,儘管我可能會將該方法重新命名為“DebugPrint”。

我認為至少部分原因是為了應對AmigaOS庫只能向前版本化並且必須保持100%向後相容的“問題”。重置介面以刪除已棄用的內容的唯一真正方法是在庫名稱中新增版本,然後您將陷入大量庫中。當然,除了AmigaOS專用的庫之外,沒有其他實際的方法可以做任何其他事情,因此這個想法的實用性可能並不大。我認為它也用於MMU支援等系統特定的東西。

但與一般的系統庫一樣,它並不是為應用程式程式設計而設計的解決方案,這就是建立BOOPSI的原因。

請注意,“基於C物件”的機制(即結構體巢狀結構體)也存在許多缺點。這就是glib和gtk+所使用的,經過大量的過度工程,它們設法解決了大多數問題——例如結構體大小增長破壞二進位制相容性(儲存一個單獨的私有資料塊,該塊被分配並使用得更像BOOPSI分配物件的方式),新增介面(在執行時從簡單函式呼叫的轉換中查詢它),屬性處理程式、事件等等。但它很大、很慢、難以使用,需要大量的樣板程式碼,而且總的來說不太好……我已經幾年沒有使用它了,但我認為核心自那時起變化不大。

  • 每個公共函式(以及大多數內部函式,唉)都有一個“轉換”宏,它使用樹掃描或線性掃描(如果我記得沒錯)來檢查物件的類型別(實際上,大多數物件的“使用者”會進行另一次執行此操作的轉換)。

同樣也會進行檢查——因此錯誤訊息會指向原始碼……)(並且通常每個成員訪問要麼也使用宏,要麼呼叫執行此操作的其他函式,或者使用get/set介面)。

  • 私有資料需要類查詢(與BOOPSI的資料指標相同),並且還意味著每個物件例項化至少分配了兩個記憶體塊。
  • 如果我記得沒錯,每個“屬性”都以一個字串為鍵,它必須在全域性表中查詢該字串以轉換為動態整數,然後需要一個if/then if/else樹來處理,因為它不是靜態的(即比BOOPSI糟糕得多)。
  • 事件處理是一場噩夢,我不認為任何人值得知道它是如何工作的。

請注意,很多混亂據說是為了支援對除c以外的其他事物的語言繫結。

可以透過更簡單的實現來修復其中的一些問題,但您仍然會遇到一些嚴重的限制,因為它本質上是靜態繫結。因此,它仍然難以應對版本控制和二進位制相容性以及動態介面。

x86特別擅長執行糟糕的分支程式碼,而且CPU本身也很快,所以我懷疑轉向另一種過時的技術是否真的值得,除非您獲得了很大的收益。當您新增所有必要的負擔時,我有一種預感,您可能不會。總是有權衡……

也取決於要解決的問題。例如,“我想用c++編寫/呼叫系統庫”與“我想建立一個基於c的應用程式級物件系統”不同。

我對我們當前的oop.library實現不滿意。因此,我沒有投入任何精力,因為我首先想改進整個OO系統。我發現方法呼叫期間仍然發生了太多事情。我有一些想法,但沒有時間測試我的想法是否有意義。如果有人想看看,我可以嘗試寫下我粗略且模糊的想法。

參考文獻

[編輯 | 編輯原始碼]
APTR OOP_NewObject(struct OOP_IClass *classPtr, UBYTE *classID, struct TagItem *tagList) 
OOP_AttrBase OOP_ObtainAttrBase(STRPTR interfaceID) 
OOP_MethodID OOP_GetMethodID(STRPTR interfaceID, ULONG methodOffset) 
void OOP_AddClass(OOP_Class *classPtr) 
void OOP_ReleaseAttrBase(STRPTR interfaceID) 
void OOP_DisposeObject(OOP_Object *obj) 
void OOP_RemoveClass(OOP_Class *classPtr)

OOP_AttrBase OOP_GetAttrBase(STRPTR interfaceID) 
IPTR OOP_GetAttr(OOP_Object *object, OOP_AttrID attrID, IPTR *storage) 
IPTR OOP_SetAttrs(OOP_Object *object, struct TagItem *attrList) 
BOOL OOP_ObtainAttrBases(struct OOP_ABDescr *abd) 
void OOP_ReleaseAttrBases(struct OOP_ABDescr *abd) 
LONG OOP_ParseAttrs(struct TagItem *tags, IPTR *storage, ULONG numattrs, OOP_AttrCheck *attrcheck, OOP_AttrBase attrbase) 
void *OOP_GetMethod(OOP_Object *obj, OOP_MethodID mid) 
華夏公益教科書