2009-04-20 54 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
  • 轨道/程序应该知道它在父结构中的哪个索引?

副手我不认为有任何理由要对此作出规定。这很难不例子说...

  • 结构的这种层次结构看起来相当复杂,但另一种方法是 传递程序和/或跟踪指数 各种功能。哪个是 更可取?

你可以举个例子吗?即使是几个函数声明也可能有助于清除它。

  • 还有其他一般性意见吗?

如果这真是一个只读的结构,你不必太在意在未来多线程。