考虑一个简单,可重复使用的库。它有一个当前状态的对象, 以及一个回调函数来输入它。
typedef struct Context_S Context_T;
typedef size_t (*GetBytes_T) (Context_T * ctx, uint8_t * bytes, size_t max);
struct Context_S {
GetBytes_T byteFunc;
void * extra;
// more elements
};
void Init(Context_T * ctx, GetBytes_T func);
int GetNext(Context_T * ctx); // Calls callback when needs more bytes
用户可能需要一些额外的数据用于回调(如文件指针)。图书馆 提供了一个额外指针的函数:
void SetExtra(Context_T * ctx, void * ext); // May be called after init
void * GetExtra(Context_T const * ctx); // May be called in callback
但是,如果用户额外数据是常量,则需要他强制转换constness 在设置数据之前离开。我可以改变函数来取/返const, 但如果数据不应该是常数,这将需要在回调中额外投射。
void SetExtra(Context_T * ctx, void const * ext);
void const * GetExtra(Context_T const * ctx);
第三种选择是在函数调用中隐藏强制转换:
void SetExtra(Context_T * ctx, void const * ext);
void * GetExtra(Context_T const * ctx);
在这种情况下隐藏演员是否是个好主意?
我正试图找到可用性和类型安全性的平衡点。但既然我们是
使用void*
指针,很多安全措施已经消失。
或者我忽略了值得考虑的事情?
答案 0 :(得分:5)
C标准库存在类似问题。众所周知,strchr
函数接受const char *
参数并返回指向给定字符串的char *
值。
这是C语言的一个缺陷:它对const
的规定不支持合理使用const
的所有方式。
遵循C标准的示例是不合理的:接受指向const
的指针,并在将其返回给调用软件时,提供指向非const
的指针,如你的第三个例子。
另一种方法是定义两组例程,SetExtra
和GetExtra
使用非const
,SetExtraConst
和GetExtraConst
使用{{ 1}}。这些可以在运行时强制执行,并使用额外的位来记录设置的上下文是const
还是非 - const
。但是,即使没有强制执行,它们也可能会有所帮助,因为它们可能会使调用代码中的错误更加明显:阅读代码的人可能会看到const
用于设置数据和SetExtraConst
(非{ {1}})用于获取数据。 (如果调用代码有些复杂并且在某些情况下使用GetExtra
数据而在其他情况下使用非const
数据,这可能无济于事,但最好是捕获更多错误而不是更少。)
答案 1 :(得分:1)
对于标准的“hack away”功能程序设计,它非常简单:
但在您的情况下,您似乎正在进行适当的面向对象设计,其中您的代码模块是唯一知道Context_T
是什么以及它包含什么的人。 (我认为第一行的typedef实际上是在h文件中?)
如果是这样,你不能也不应该指针const
。特别是如果你使用不完整类型(“opaque”类型)实现真正的OO封装,因为在这种情况下调用者无论如何都不能修改内容:“const correctness”变得多余。