目录

分层设计:    

分析:

使用:

 拓展:


分层设计:    

      今天在看源码时,代码分层设计深深吸引了我。就拿一个简单的输出举例。输出一般有三种方式stdout、socket(网络打印调试信息)、log日志。我们如何将它们揉在一块呢。

分析:

如下是它的结构图

                                              代码分层设计

接下来我们从外到内进行分析:这是项目布局

                                                   代码分层设计

       这里面有两种打印方式,一种是stdout,一种是socket网络打印,而debug_manager.c就是用来管理它两的,是不是发现这样写很容易扩展呢,自己还可以添加一个localog.c(日志)。

这与平常最大的不同是有debug_manager.c文件,我们对它进行分析分析。

typedef struct DebugOpr {
	char *name;                  /*打印类型 stdout、netprint*/
	int isCanUse;             
	int (*DebugInit)(void);      /* 调试模块的初始化函数 */
	int (*DebugExit)(void);      /* 退出函数 */
	int (*DebugPrint)(char *strData);  /* 输出函数 */
	struct DebugOpr *ptNext;
}T_DebugOpr, *PT_DebugOpr;

  stdout.c文件

static int StdoutDebugPrint(char *strData)
{
	/* 直接把输出信息用printf打印出来 */
	printf("%s", strData);
	return strlen(strData);	
}

static T_DebugOpr g_tStdoutDbgOpr = {
	.name       = "stdout",
	.isCanUse   = 1,                 /* 1表示将使用它来输出调试信息 */
	.DebugPrint = StdoutDebugPrint,  /* 打印函数 */
};

int StdoutInit(void)
{
	return RegisterDebugOpr(&g_tStdoutDbgOpr);
}

 netprint.c文件

static int NetDbgInit(void)
{
	/* socket初始化 */
	int iRet;
	
	g_iSocketServer = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == g_iSocketServer)
	{
		printf("socket error!\n");
		return -1;
	}

	g_tSocketServerAddr.sin_family      = AF_INET;
	g_tSocketServerAddr.sin_port        = htons(SERVER_PORT);  /* host to net, short */
 	g_tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
	memset(g_tSocketServerAddr.sin_zero, 0, 8);
	
	iRet = bind(g_iSocketServer, (const struct sockaddr *)&g_tSocketServerAddr, sizeof(struct sockaddr));
	if (-1 == iRet)
	{
		printf("bind error!\n");
		return -1;
	}

	g_pcNetPrintBuf = malloc(PRINT_BUF_SIZE);
	if (NULL == g_pcNetPrintBuf)
	{
		close(g_iSocketServer);
		return -1;
	}


	/* 创建netprint发送线程: 它用来发送打印信息给客户端 */
	pthread_create(&g_tSendTreadID, NULL, NetDbgSendTreadFunction, NULL);			
	
	/* 创建netprint接收线否: 用来接收控制信息,比如修改打印级别,打开/关闭打印 */
	pthread_create(&g_tRecvTreadID, NULL, NetDbgRecvTreadFunction, NULL);			

	return 0;	
}
static int NetDbgExit(void)
{
	/* 关闭socket,... */
	close(g_iSocketServer);
	free(g_pcNetPrintBuf);
	return 0;
}
static int NetDbgPrint(char *strData)
{
	/* 把数据放入环形缓冲区 */
	int i;
	
	for (i = 0; i < strlen(strData); i++)
	{
		if (0 != PutData(strData[i]))
			break;
	}
	
	/* 如果已经有客户端连接了, 就把数据通过网络发送给客户端 */
	/* 唤醒netprint的发送线程 */
	pthread_mutex_lock(&g_tNetDbgSendMutex);
	pthread_cond_signal(&g_tNetDbgSendConVar);
	pthread_mutex_unlock(&g_tNetDbgSendMutex);

	return i;
	
}
static T_DebugOpr g_tNetDbgOpr = {
	.name       = "netprint",
	.isCanUse   = 1,
	.DebugInit  = NetDbgInit,
	.DebugExit  = NetDbgExit,
	.DebugPrint = NetDbgPrint,
};
int NetPrintInit(void)
{
	return RegisterDebugOpr(&g_tNetDbgOpr);
}

使用:

char strFileName[256];
memset(strFileName,0,sizeof(strFileName));
strcpy(strFileName,"hello");
DebugInit();
//在需要打印的地方,输上这句话就可以用了
DBG_PRINTF("<7>hi %s error!\n", strFileName);

 拓展:

 有没有一种疑问,这么写的代码咋用makefile写呢,模块里面也有一个makefile文件。

AS		= $(CROSS_COMPILE)as   #编译选项
LD		= $(CROSS_COMPILE)ld   #链接选项
CC		= $(CROSS_COMPILE)gcc
CPP		= $(CC) -E             

STRIP		= $(CROSS_COMPILE)strip
OBJCOPY		= $(CROSS_COMPILE)objcopy
OBJDUMP		= $(CROSS_COMPILE)objdump

export AS LD CC CPP 
export STRIP OBJCOPY OBJDUMP

CFLAGS := -Wall -Werror -O2 -g                  
CFLAGS += -I $(shell pwd)/include               #头文件

LDFLAGS := -lm - -lpthread                      #库文件

export CFLAGS LDFLAGS

TOPDIR := $(shell pwd)                          #顶层目录
export TOPDIR

TARGET := Debug                                 #目标名字

obj-y += main.o                                 
obj-y += debug/
all : 
	make -C ./ -f $(TOPDIR)/Makefile.build
	$(CC) $(LDFLAGS) -o $(TARGET) built-in.o
clean:
	rm -f $(shell find -name "*.o")
	rm -f $(TARGET)
distclean:
	rm -f $(shell find -name "*.o")
	rm -f $(shell find -name "*.d")
	rm -f $(TARGET)

  解析:-Wall -Werror -O2 -g (Werror就是把warning 当error处理)

-Wall:选项可以打印出编译时所有的错误或者警告信息。这个选项很容易被遗忘,编译的时候,没有错误或者警告提示,以为自己的程序很完美,其实,里面有可能隐藏着许多陷阱。变量没有初始化,类型不匹配,或者类型转换错误等警告提示需要重点注意,错误就隐藏在这些代码里面。没有使用的变量也需要注意,去掉无用的代码,让整个程序显得干净一点。下次写Makefile的时候,一定加-Wall编译选项。

-O0: 表示编译时没有优化。

-O1: 表示编译时使用默认优化。

-O2: 表示编译时使用二级优化。

-O3: 表示编译时使用最高级优化。

相关文章: