一:带检查功能的栈
1.实现
stack.h
#ifndef _STACK_H_
#define _STACK_H_
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/*栈结构体中加入范围检查*/
typedef struct {
int top;
const size_t size;
int * const pBuf;
const bool needRangeCheck;
const int min;
const int max;
} Stack;
bool push(Stack *p, int val);
bool pop(Stack *p, int *pRet);
#define newStack(buf) { \
0, sizeof(buf) / sizeof(int), (buf), \
false, 0, 0 \
}
#define newStackWithRangeCheck(buf, min, max) { \
0, sizeof(buf) / sizeof(int), (buf), \
true, min, max \
}
#ifdef __cplusplus
}
#endif
#endif
stack.c
#include <stdbool.h>
#include "stack.h"
static bool isStackFull(const Stack *p) {
return p->top == p->size;
}
static bool isStackEmpty(const Stack *p) {
return p->top == 0;
}
static bool isRangeOk(const Stack *p, int val) {
return ! p->needRangeCheck ||
(p->min <= val && val <= p->max);
}
// true: 成功, false: 失敗
bool push(Stack *p, int val) {
if (! isRangeOk(p, val) || isStackFull(p)) return false;
p->pBuf[p->top++] = val;
return true;
}
// true: 成功, false: 失敗
bool pop(Stack *p, int *pRet) {
if (isStackEmpty(p)) return false;
*pRet = p->pBuf[--p->top];
return true;
}
使用:
int buf[16];
Stack stack = newStackWithRangeCheck(buf,0,9);
2.分析:
如果只是这样简单的功能,删除方法足够应对了。但是,上面的代码中仍然存在如下问题:
- 即使要生成的的不带范围检查功能的栈,栈内也需要保存needRangeCheck、min、max等多余的结构体成员,浪费了内存
- 如果还想在栈内增加其他校验功能,就必须在结构体内再增加其他成员。这样就必须在结构体内保存所有检查功能的成员,push函数也会因为这些功能变的臃肿,最终导致栈越来越难以应对功能的增加
那么,这次增加的结构体成员needRangeCheck、min、max真的有必要和结构体的其他成员放在相同的位置吗? 这些成员只在isRangeOk函数中被使用,将他们作为结构体成员让所有函数都可以看到是不合适的,也就是说存在作用域污染问题。那么我们需要把这些成员分离出去。
二.将范围检查分离出来的栈
1.实现
stack.h
#ifndef _STACK_H_
#define _STACK_H_
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/*将范围检查从栈结构体中独立出来*/
typedef struct {
const int min;
const int max;
}Range;
typedef struct {
int top;
const size_t size;
int * const pBuf;
const Range *const pRange;
} Stack;
bool push(Stack *p, int val);
bool pop(Stack *p, int *pRet);
#define newStack(buf) { \
0, sizeof(buf) / sizeof(int), (buf), \
NULL \
}
#define newStackWithRangeCheck(buf, pRange) { \
0, sizeof(buf) / sizeof(int), (buf), \
pRange \
}
#ifdef __cplusplus
}
#endif
#endif
stack.c
#include <stdbool.h>
#include "stack.h"
static bool isStackFull(const Stack *p) {
return p->top == p->size;
}
static bool isStackEmpty(const Stack *p) {
return p->top == 0;
}
static bool isRangeOk(const Range *p, int val) {
return ! p == NULL ||
(p->min <= val && val <= p->max);
}
// true: 成功, false: 失敗
bool push(Stack *p, int val) {
if (! isRangeOk(p->pRange, val) || isStackFull(p)) return false;
p->pBuf[p->top++] = val;
return true;
}
// true: 成功, false: 失敗
bool pop(Stack *p, int *pRet) {
if (isStackEmpty(p)) return false;
*pRet = p->pBuf[--p->top];
return true;
}
三.通用化校验器的栈
1.实现
stack.h
#ifndef _STACK_H_
#define _STACK_H_
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/*加入校验器,将栈中要检查数据的职责全部转移到Validator结构体中
*第一个成员是函数指针,该函数的参数为指向Validator结构体的指针和需要校验的参数,并返回bool类型
*第二个成员为校验时所需要的数据,由于校验类型不同,所以被校验数据的类型也不尽相同,为了能保存任意*类型的数据,这里使用void指针
*/
typedef struct _Validator {
bool (* const validate)(struct _Validator *pThis, int val);
void * const pData;
} Validator;
typedef struct {
const int min;
const int max;
} Range;
typedef struct {
int previousValue;
} PreviousValue;
typedef struct {
int top;
const size_t size;
int * const pBuf;
Validator * const pValidator;
} Stack;
bool validateRange(Validator *pThis, int val);
bool validatePrevious(Validator *pThis, int val);
bool push(Stack *p, int val);
bool pop(Stack *p, int *pRet);
#define newStack(buf) { \
0, sizeof(buf) / sizeof(int), (buf), \
NULL \
}
#define rangeValidator(pRange) { \
validateRange, \
pRange \
}
#define previousValidator(pPrevious) { \
validatePrevious, \
pPrevious \
}
#define newStackWithValidator(buf, pValidator) { \
0, sizeof(buf) / sizeof(int), (buf), \
pValidator \
}
#ifdef __cplusplus
}
#endif
#endif
stack.c
#include <stdbool.h>
#include "stack.h"
static bool isStackFull(const Stack *p) {
return p->top == p->size;
}
static bool isStackEmpty(const Stack *p) {
return p->top == 0;
}
bool validateRange(Validator *pThis, int val) {
Range *pRange = (Range *)(pThis->pData);
return pRange->min <= val && val <= pRange->max;
}
bool validatePrevious(Validator *pThis, int val) {
PreviousValue *pPrevious = (PreviousValue *)pThis->pData;
if (val < pPrevious->previousValue) return false;
pPrevious->previousValue = val;
return true;
}
bool validate(Validator *p, int val) {
if (! p) return true;
return p->validate(p, val);
}
// true: 成功, false: 失敗
bool push(Stack *p, int val) {
if (! validate(p->pValidator, val) || isStackFull(p)) return false;
p->pBuf[p->top++] = val;
return true;
}
// true: 成功, false: 失敗
bool pop(Stack *p, int *pRet) {
if (isStackEmpty(p)) return false;
*pRet = p->pBuf[--p->top];
return true;
}
- 如果使用范围检查,则pData指向Range结构体
- 如果是push值递增的校验,则pData指针接收的数据为保存上次push的值的结构体
- 函数指针所指向的校验器函数。Validator的pData成员是void指针,因此先通过类型转换取出校验时所需要的值,然后进行校验
测试:
#include "gtest/gtest.h"
#include <stdbool.h>
#include "stack.h"
TEST(StackTest, popFromEmptyStackReturnsFalse) {
int buf[16];
Stack stack = newStack(buf);
EXPECT_EQ(false, pop(&stack, 0));
}
TEST(StackTest, popReturnsStackTopAndRemoveIt) {
int buf[16];
Stack stack = newStack(buf);
EXPECT_EQ(true, push(&stack, 123));
int ret;
EXPECT_EQ(true, pop(&stack, &ret));
EXPECT_EQ(123, ret);
EXPECT_EQ(false, pop(&stack, &ret));
}
TEST(StackTest, pushToFullStackReturnsFalse) {
int buf[16];
Stack stack = newStack(buf);
for (int i = 0; i < 16; ++i) push(&stack, i);
EXPECT_EQ(false, push(&stack, 100));
int ret;
EXPECT_EQ(true, pop(&stack, &ret));
EXPECT_EQ(15, ret);
}
TEST(StackTest, pushWithRangeCheck) {
int buf[16];
Range range = {0, 9};
Validator validator = rangeValidator(&range);
Stack stack = newStackWithValidator(buf, &validator);
EXPECT_EQ(false, push(&stack, -1));
EXPECT_EQ(false, push(&stack, 10));
}
TEST(StackTest, pushWithPreviousCheck) {
int buf[16];
PreviousValue previous = {0};
Validator validator = previousValidator(&previous);
Stack stack = newStackWithValidator(buf, &validator);
EXPECT_EQ(true, push(&stack, 3));
EXPECT_EQ(false, push(&stack, 2));
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}