软件工程课程实验报告:实验七

实验七:将menu设计为可重用的子系统

咖啡机《软件工程(C编码实践篇)》MOOC课程作业http://mooc.study.163.com/course/USTC-1000002006

新创建一个目录lab7完成实验。


一、实验要求

  • 为menu子系统设计接口,并写用户范例代码来实现原来的功能;
  • 使用make和make clean来编译程序和清理自动生成的文件;
  • 使menu子系统支持带参数的复杂命令,并在用户范例代码中自定义一个带参数的复杂命令;
  • 可以使用getopt()函数获取命令行参数。

二、实验过程

1. 设计menu子系统接口

menu.h添加两个函数MenuConfig()ExecuteMenu()用于添加命令和运行menu程序

/* add cmd to menu */
int MenuConfig(char* cmd, char* desc, int (*handler)(int argc, char *argv[]));
/* Menu Engine Execute */
int ExecuteMenu();
2. 子接口实现

menu.c中实现MenuConfig()函数和ExecuteMenu()函数

/* Set or configure menu list */
int MenuConfig(char* cmd, char* desc, int (*handler)(int argc, char *argv[]))
{
    tDataNode *pNode = NULL;
    if(head == NULL)
    {
        head = CreateLinkTable();
        pNode = (tDataNode*)malloc(sizeof(tDataNode));
        pNode->cmd = "help";
        pNode->desc = "This is the informations of commands";
        pNode->handler = Help;
        AddLinkTableNode(head, (tLinkTableNode*)pNode);
    }
    pNode = (tDataNode*)malloc(sizeof(tDataNode));
    pNode->cmd = cmd;
    pNode->desc = desc;
    pNode->handler = handler;
    AddLinkTableNode(head, (tLinkTableNode*)pNode);
    return 0;
}
/* menu program */
int ExecuteMenu()
{
   /* cmd line begins */
    while(1)
    {
        int argc = 0;
        char *argv[CMD_MAX_ARGV_NUM];
        char cmd[CMD_MAX_LEN];
        char *pcmd = NULL;
        printf("Input a command> ");
        pcmd = fgets(cmd, CMD_MAX_LEN, stdin);
        tDataNode *p = FindCmd(head, cmd);
        if( pcmd == NULL)
        {
            continue;
        }
        pcmd = strtok(pcmd, " ");
        while(pcmd != NULL && argc < CMD_MAX_ARGV_NUM)
        {
            argv[argc] = pcmd;
            argc ++;
            pcmd = strtok(NULL, " ");
        }
        if(argc == 1)
        {
            int len = strlen(argv[0]);
            *(argv[0] + len -1) = '\0';
        }
        tDataNode *pn = (tDataNode*)SearchLinkTableNode(head, SearchCondition, (void*)argv[0]);
        if(pn == NULL)
        {
            printf("This is a wrong cmd!\n");
            continue;
        }
        if(pn->handler != NULL)
        {
            pn->handler(argc, argv);
        }
    }
    return 0;
}
3. 定义带参数的指令

利用getopt()获取命令参数,为命令定义不同参数下的不同执行操作。

int Help(int argc,char* argv[])
{
    int opt;
    while((opt=getopt(argc,argv,"l:a:"))!= -1)
    {
        switch(opt)
        {
            case 'a':
                printf("this is -a option\n");
                ShowAllCmd(head);
                break;
            case 'b':
                printf("this is -b option\n");
                break;
            default:
                printf("Unknowed  option %c\n",opt);
        }    
    }
    return 0;
}
4. 测试模块实现

主要由test.c承担,调用menu.c中的接口,添加和调用指令。其中,自定义的指令包括:

  • version
  • exit
  • plus
  • minus
  • multiply
  • divide
#include 
#include 
#include 
#include 
#include "linktable.h"
#include "menu.h"

int Exit(int argc, char *argv[])
{
    printf("exit the program\n");
    exit(0);
}

int Version(int argc, char *argv[])
{
    printf("menu program v3.0\n");
}

int plus(int argc, char *argv[])
{
    float a,b;
    printf("Input two numbers:\n");
    scanf("%f", &a);
    scanf("%f", &b);
    printf("The sum of %f and %f is %f\n", a, b, a+b);
}

int minus(int argc, char *argv[])
{
    float a,b;
    printf("Input two numbers:\n");
    scanf("%f", &a);
    scanf("%f", &b);
    printf("%f minus %f equals %f\n", a, b, a-b);
}

int multiply(int argc, char *argv[])
{
    float a,b;
    printf("Input two numbers:\n");
    scanf("%f", &a);
    scanf("%f", &b);
    printf("%f multiply %f equals %f\n", a, b, a*b);
}

int divide(int argc, char *argv[])
{
    float a,b;
    printf("Input two numbers:\n");
    scanf("%f", &a);
    scanf("%f", &b);
    if ( b == 0)
        printf("Error: 0 can not be used as a divisor!\n");
    else
        printf("%f divided by %f is %f\n", a, b, a/b);
}

int main(int argc, char *argv[])
{
    MenuConfig("version", "menu program v3.0", Version);
    MenuConfig("plus", "sum of a and b", plus);
    MenuConfig("minus", "result of a minus b", minus);
    MenuConfig("multiply", "result of a times b", multiply);
    MenuConfig("divide", "result of a divided by b", divide);
    MenuConfig("exit", "Exit this program", Exit);
    ExecuteMenu();
    return 0;
}
5. 编辑Makefile

使用makemake clean来编译程序和清理自动生成的文件。

CC_PTHREAD_FLAGS     = -lpthread
CC_FLAGS             = -c
CC_OUTPUT_FLAGS      = -o
CC_MATH              = -lm
CC                   = gcc
RM                   = rm
RM_FLAGS             = -f

TARGET               = test
OBJS                 = linktable.o menu.o test.o

all:    $(OBJS)
    $(CC) $(CC_OUTPUT_FLAGS) $(TARGET) $(OBJS) $(CC_MATH)

.c.o:
    $(CC) $(CC_FLAGS) $<

clean:
    $(RM) $(RM_FLAGS) $(OBJS) $(TARGET) *.bak
6. 编译运行

包括三部分:
- 利用make指令编译程序
- 执行程序,并利用help -aversionexit等三个正确指令,quit一个错误指令进行测试
- 利用make clean清理程序执行过程中自动生成的文件。
软件工程课程实验报告:实验七_第1张图片

7. 将代码同步到github

软件工程课程实验报告:实验七_第2张图片

Github地址:https://github.com/973301529/se/tree/master/lab7

三、实验总结

通过此次实验,对程序进行了更进一步的可重用设计,学习了Makefile的编写方法,简化了编译命令。

你可能感兴趣的:(软件工程实验报告)