我有一个库函数,它接受一个level
参数,并使用它来索引一个参数值数组,我正在从函数的使用者中抽象出来。请注意,参数表,甚至其结构类型仅对其包含的C文件可见。
library.h
#define MIN_LEVEL 0
#define MAX_LEVEL ((sizeof(m_param_table)/sizeof(m_param_table[0]))-1)
BOOL set_param_level(int level);
LIBRARY.C
#include "library.h"
typedef struct {
int param1;
int param2;
} params_t;
// Parameters table - static (local) to this C file
static params_t m_param_table[] = {
{0, 1},
{2, 3},
};
BOOL set_param_level(int level) {
int p1, p2;
// bounds checking on MIN_LEVEL and MAX_LEVEL
if (level < MIN_LEVEL) return FALSE;
if (level > MAX_LEVEL) return FALSE;
p1 = m_param_table[level].param1;
p2 = m_param_table[level].param2;
// do stuff with p1, p2
return TRUE;
}
consumer.c
#include "library.h"
// Limit user input to MIN_LEVEL and MAX_LEVEL
set_param_level( user_input_value );
我希望consumer.c能够访问MIN_LEVEL
和MAX_LEVEL
,以进行用户输入验证。但显然,他无法访问m_param_table
,因此这些宏不起作用。
最优雅且正确的方式是什么?选项包括:
1)将typedef ... params_t
移至标头文件,然后从static
移除m_params_table
。显然我不喜欢这样,因为它会使这些东西不必要地显而易见。
2)对头文件中的值进行硬编码。当然,硬编码的数据很糟糕。
答案 0 :(得分:5)
您有第三种选择:
将MIN_LEVEL
和MAX_LEVEL
移至library.c
并创建get_min_level()
中声明并在get_max_level()
中实施的新功能library.h
和library.c
只需分别返回MIN_LEVEL
和MAX_LEVEL
。这些函数将在consumer.c
中显示,而不会泄露库的内部数据结构。
答案 1 :(得分:1)
编辑:添加了const关键字
一种选择是使用外部。
首先,MIN_LEVEL和MAX_LEVEL不应该在library.h中,而应该在library.c中。正如您所指出的,没有m_param_table的定义,消费者无法使用此头文件。而且你不应该只是为了提供对这些常量的访问而公开。这就是你能做的。
在library.h中:
extern const int min_level;
extern const int max_level;
BOOL set_param_level(int level);
在library.c中:
...
#define MIN_LEVEL 0
#define MAX_LEVEL ((sizeof(m_param_table)/sizeof(m_param_table[0]))-1)
const int min_level = MIN_LEVEL;
const int max_level = MAX_LEVEL;
这使得常量可供消费者使用,而不会暴露私人内部。
答案 2 :(得分:1)
好吧,如果你希望你的值是常量,同时你更喜欢隐藏表和相关的类型声明,那么一个解决方案就是确实对这些值进行硬编码,但同时添加.c
文件中的静态断言,确保硬编码值始终是最新的。
所以,在头文件中你做
#define MIN_LEVEL 0
#define MAX_LEVEL 1
在.c
文件中
static params_t m_param_table[] = {
{0, 1},
{2, 3},
};
STATIC_ASSERT(MIN_LEVEL == 0);
STATIC_ASSERT(MAX_LEVEL == sizeof m_param_table / sizeof *m_param_table - 1);
(在C中使用您最喜欢的STATIC_ASSERT
实现。
这种方法消除了硬编码值吮吸的最重要原因之一:他们倾向于悄然过时。