2009-04-20 56 views
2

我正在編寫我的第一個主要C API,並且我想讓事情正確。該庫爲內部結構分配和釋放內存 - 使用typedef從客戶端隱藏內存。我提供訪問數據的粗略的結構是這樣的:適用於光盤層次結構的C API設計 - 最佳實踐

盤 - >程序 - >軌道

相關與光盤事情像文件描述符讀取,文件大小,數量程序和一些光盤範圍的屬性。

與程序相關的是諸如(程序)索引到光盤中的物體,文件中的物理偏移量,軌道數量和名稱。

與軌道相關的事物包括程序中的(軌道)索引,名稱以及文件中的一組偏移量。

每個結構都有一個指向父結構的指針。

我有幾個問題,但我會盡力保持簡短:

  • 如果音軌/程序知道它在父結構是在該指數?
  • 這個結構層次結構看起來相當複雜,但另一種方法是將程序和/或軌道索引傳遞給各種函數。哪個更好?
  • 還有其他一般性意見嗎?

這是一個類似於文件系統的結構(但它是隻讀的),我希望它在將來與多線程兼容,並且它需要是可移植的。我在這裏特別談論C - 沒有C++。

+1

* facepalm *感謝查理 – Draemon 2009-04-20 23:59:48

回答

3

大量編輯介紹我最初批評了問題描述爲混亂。我的問題源於問題中使用「文件」的概念。這個問題意味着光盤,程序和曲目都存儲在一個'文件'中。我認爲提問者正在構建他自己的文件系統,這會讓這個「一個文件中的所有東西」變得很奇怪,但是我現在已經認定他可能沒有這樣做,在這種情況下,它並不奇怪。所以,我會繼續前進,根據他使用現有(可能是標準)文件系統的假設提供一個真正的答案,並且他的整個數據結構都存儲在該文件系統中的一個文件中。如果我錯誤地認爲我會糾正的。

我會首先爲這種情況提供一個一般建議;首先從API用戶的角度來看待事物。然後設計您的API,以便他編寫的代碼輕鬆流動,無需處理您的域中正確的詳細信息。

處理API設計的一種方法是首先編寫一些用戶代碼並定義API,以便該代碼易於編寫。作爲獎勵,在您實際實現API之後,您將擁有一些測試代碼來試用。

轉向更具體的建議;

這是系統中三種數據類型的目錄。如果你喜歡,我們可以把它們當作抽象數據類型或'對象'來對待,並且定義一個typdef'ed結構體(DISC,PROGRAM,TRACK)來表示每一個。

disc = a collection of programs stored in a file 
+-----------+ 
|file  | 
+-----------+ 
|program | 
+-----------+ 
|program | 
+-----------+ 
|...  | 
+-----------+ 
|program | 
+-----------+ 

program = a collection of tracks 
+-----------+ 
|ptr->disc | 
+-----------+ 
|name  | 
+-----------+ 
|file offset| 
+-----------+ 
|track  | 
+-----------+ 
|track  | 
+-----------+ 
|...  | 
+-----------+ 
|track  | 
+-----------+ 

track = a collection of audio samples 
+------------------+ 
|ptr->program  | 
+------------------+ 
|name    | 
+------------------+ 
|file offset+length| 
+------------------+ 
|file offset+length| 
+------------------+ 
|...    | 
+------------------+ 
|file offset+length| 
+------------------+ 

我建議你讓你的用戶挑選出從結構數據。你不能真正隱藏C語言中的結構的內部元素(沒有通過強制轉換等方式),但是你可以提供一系列函數讓你的用戶在不訪問抽象類型的內容的情況下做他們需要做的事情他們自己。例如,我們的功能家族可能看起來像這樣;

// DISC functions 
DISC  *dopen( const char *disc_name); 
void  dstats(int *ptr_nbr_programs, FILE **ptr_file); 
void  dclose(DISC *disc); 

// PROGRAM functions 
PROGRAM *popen_name(DISC *disc, const char *program_name); 
PROGRAM *popen_idx (DISC *disc, int program_idx); 
void  pstats(int *ptr_nbr_tracks); 
void  pclose(PROGRAM *program); 

// TRACK functions 
TRACK  *topen_name(PROGRAM *program, const char *track_name); 
TRACK  *topen_idx (PROGRAM *program, int track_idx); 
int  tread(unsigned char *buf, int nbytes_to_read); 
void  tseek(unsigned long offset); 
void  tclose(TRACK *track); 

這應該都是合理的自我解釋 - 它是模仿現有的標準C FILE範例。

首先你的用戶用dopen()獲得一個ptr到DISC。假設這是有效的(如果不這樣做,它將返回NULL),他可以使用dstats()獲取任何全局DISC信息。更重要的是,他可以通過popen()函數族之一獲得DISC中PROGAM的ptr。

隨着程序的一個ptr,他可以進一步鑽取並獲得一個具有topen()系列函數的單個TRACK。

一個非常重要的問題是,您不會讓自己的用戶遍歷音頻片段自己從TRACK中獲取數據。用戶提供一個緩衝區來讀取樣本,並根據需要通過碎片進行迭代以填充緩衝區。 tseek()函數提供給他隨機訪問。

我還沒有試圖找出每個參數的詳細信息以及如何處理錯誤等,我只是提出了一個概念來提煉。

請注意,'三明治'範式貫穿始終。每種類型的介紹性「開放式」和結束式「近距離」三明治操作。

+0

這是一個與多軌錄音機的文件系統交互的API。磁盤意味着硬盤,程序意味着多音軌錄音和音軌意味着音軌的單個音軌,並且每個音軌具有指向音頻碎片位的多個(偏移量,計數)對。這是否澄清了一切? – Draemon 2009-04-21 00:17:33

0
  • 軌道/程序應該知道它在父結構中的哪個索引?

副手我不認爲有任何理由要對此作出規定。這很難不例子說...

  • 結構的這種層次結構看起來相當複雜,但另一種方法是 傳遞程序和/或跟蹤指數 各種功能。哪個是 更可取?

你可以舉個例子嗎?即使是幾個函數聲明也可能有助於清除它。

  • 還有其他一般性意見嗎?

如果這真是一個只讀的結構,你不必太在意在未來多線程。