我正在编写我的第一个主要的C API,我想把事情弄清楚。该库为内部结构分配和释放内存 - 使用typedef从客户端隐藏它。我提供访问的数据的粗略结构是:
盘状>程序 - >轨道
与光盘相关的内容包括要读取的文件描述符,文件大小,程序数量以及某些光盘范围的属性。
与程序相关的东西就像(程序)索引到光盘,物理偏移到文件,轨道数和名称。
与曲目相关联的是(跟踪)索引到程序,名称和一堆偏移到文件中。
每个结构都有一个指向父结构的指针。
我有几个问题,但我会尽量保持简短:
这是一个类似文件系统的结构(但它是只读的),我希望它在未来与多线程兼容,并且它需要是可移植的。我在这里特别谈论C - 没有C ++。
答案 0 :(得分:3)
重度编辑介绍 我最初批评问题描述令人困惑。我的问题源于问题中使用“文件”的概念。问题意味着光盘,程序和曲目都存储在一个“文件”中。我认为提问者正在构建自己的文件系统,这会使“一切文件中的所有内容”结构变得奇怪,但我现在已经决定他可能不会这样做,在这种情况下它不那么奇怪。因此,基于他使用现有(可能是标准的)文件系统的假设,我将继续提供真正的答案,并且他的整个数据结构存储在该文件系统中的一个文件中。如果我假设错了,毫无疑问我会得到纠正。
我将首先为这种情况提供一条一般性建议;首先从API用户的角度看问题。然后设计您的API,以便他将编写的代码很容易流动,而无需处理您域中正确的详细信息。
处理API设计的一种方法是首先编写一些用户代码并定义API,以便该代码易于编写。作为奖励,在您实际实施API之后,您将获得一些测试代码以试用它。
转到更具体的建议;
以下是系统中三种数据类型的目录。如果您愿意,我们可以将它们视为抽象数据类型或“对象”,并定义一个typdef结构(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()获取DISC的ptr。假设这有效(如果没有则返回NULL),他可以使用dstats()获取任何全局DISC信息。更重要的是,他可以使用popen()系列函数获得DISC中的PROGAM的ptr。
使用ptr到PROGRAM,他可以进一步向下钻取并使用topen()系列函数获得单个TRACK。
非常重要的一点是,您不要让用户自己迭代音频片段以从TRACK获取数据。用户提供了一个缓冲区来读取样本,并根据需要通过片段进行迭代以填充该缓冲区。提供了tseek()函数以便随机访问。
我没有尝试弄清楚每个参数的细节以及如何处理错误等等。我只是提出了一个需要改进的概念。
请注意,整个过程都使用“三明治”范例。每种类型的介绍性“开放式”和“结束式”三明治操作。
答案 1 :(得分:0)
- 轨道/程序是否应该知道它在父结构中的哪个索引?
副手我不认为有任何理由反对这一点。没有例子就很难说......
- 这种结构等级看起来相当复杂,但另一种选择是 传递程序和/或跟踪索引 各种功能。这是 优选?
你能举出各自的例子吗?即使是一些函数声明也可能有助于清除它。
- 更一般的评论?
如果它真的是一个只读结构,那么将来你不需要过多关注多线程。