Blu*_*ula 6 c semantic-versioning
我的 C 库,在 1.0.0 版本中,定义了一个结构体和一些用于分配和使用结构体的函数:
typedef struct { int x; int y; } MyStruct;
MyStruct *allocate( int, int );
void destroy( MyStruct* );
void print( MyStruct* );
Run Code Online (Sandbox Code Playgroud)
用户不应该自己分配结构,也不应该按值复制它。这是与语义版本控制问题的主要区别:次要还是主要变化?. 例如,程序应该像这样使用它:
void f(){
MyStruct *ms = allocate(0,0);
ms->x += 1;
print(ms);
destroy(ms);
}
Run Code Online (Sandbox Code Playgroud)
现在我需要向结构中添加一个新字段,而函数的签名不会改变。
typedef struct { int x; int y; int z; } MyStruct;
Run Code Online (Sandbox Code Playgroud)
新结构比旧结构占用更多内存:如果程序尝试MyStruct直接分配实例或按值复制它,如果它链接到与构建时使用的库版本不同的库版本,则它可能会中断。
然而,这不是程序使用的方式MyStruct:只要它们遵循文档,一切正常。但是代码中没有任何内容可以阻止他们滥用结构。
我正在使用语义版本控制来对我的库进行版本控制。在上述情况下,我应该增加次要版本(以向后兼容的方式添加的功能)还是主要版本(不兼容的 API 更改)?
您对公开可见的结构进行了重大更改,该结构在 C 中与早期版本不向后兼容。除了结构大小的变化之外,您自己的示例还说明了为什么这是一个重大变化:
void f(){
MyStruct *ms = allocate(0,0);
ms->x += 1;
print(ms); // Indicates you are aware of external dependencies.
destroy(ms);
}
Run Code Online (Sandbox Code Playgroud)
您正在公开一个数据结构,该数据结构可能由您的客户合并到某种数据集中。您可能对如何使用您的库有一些想法,但我可以向您保证,您的客户总是会给您带来惊喜。
考虑到您发布的代码,以及您声明的意图说明如何使用您的库,我想说您需要重新设计您的 API,这样MyStruct对用户来说完全不透明并且永远不会改变大小(客户经常缓存这样的东西) . 从标准库中借用一个页面,使用句柄。
typedef int MyHandle;
MyHandle allocate(int x, int y);
void destroy(MyHandle h);
void print(MyHandle h);
Run Code Online (Sandbox Code Playgroud)
句柄可以由您的内部代码进行范围检查,然后用作结构或结构指针表的索引,或者它可以是二叉树的关键。关键是你可以自由地做你喜欢做的事,而不会破坏你的 API。
如果您希望这些x y位可见,请使用可扩展结构:
typedef struct {
int x;
int y;
void* reserved; // For internal use only!
} MyStruct;
Run Code Online (Sandbox Code Playgroud)
MyStruct.reserved 字段应始终为 NULL,直到您需要它供内部使用。请记住,一旦您公开这样的数据字段,您的客户如何使用它们就完全不受您的控制了。当您以这种方式公开结构时,您就是在对您的客户做出承诺。
关于 getter 和 setter。
// Using the MyHandle type described earlier:
typedef struct _My_XY {
int x;
int y;
} My_XY;
My_XY GetXY(MyHandle h);
Run Code Online (Sandbox Code Playgroud)
问题解决了。
我要补充一点,因为z无论如何添加该字段都是一个突破性的变化,而且您似乎觉得需要扩展您的产品的功能,这将是彻底重写 API 的好时机。但是,如果您必须在不破坏客户群的情况下扩展它,您可以使用以下内容在z内部隐藏数据点:
// Public API
typedef struct { int x; int y; } MyStruct;
MyStruct *allocate( int, int );
void destroy( MyStruct* );
void print( MyStruct* );
Run Code Online (Sandbox Code Playgroud)
// Implementation
typedef struct _XY_Node {
MyStruct* xy;
int z;
struct _XY_Node *pNext;
} XY_Node;
XY_Node root = null;
XY_Node* AddNode(int x, int y, int z) {...}
XY_Node* RemoveNode(int x, int y, int z) {...}
XY_Node* FindNode(int x, int y, int z) {...}
int DeriveZ(int x, int y) {...}
MyStruct *allocate(int x, int y)
{
return AddNode(x, y, DeriveZ(x, y)).xy;
}
// etc...
Run Code Online (Sandbox Code Playgroud)
您可以使用哈希表或某种形式的二叉树,而不是列表。关键是,您不必为了扩展 API 而破坏它。您可以将其作为 1.yz 系列的最终版本进行,然后彻底修改您的 API,从而获得更清晰的客户体验和更高效的实施。