Optimization reconstruction of Switch/case cyclomatic complexity in C language

Source: Internet
Author: User

Software refactoring is a common technical means to improve code readability, extensibility, maintainability, and so on. Cyclomatic complexity, as a software quality metric, can reflect these internal quality requirements to some extent (not all of them), so cyclomatic complexity is often used as one of the metrics of software quality in many projects.

C Language development project, Switch/case code block is a very easy to cause the cyclomatic complexity of the language features, so this article mainly introduces the lower switch code snippet refactoring means (such as). The optimization reconfiguration of switch cyclomatic complexity can be divided into two parts: the reconstruction of Program block and the reconstruction of case. Program Block refactoring is a local optimization of code, while case refactoring is the overall design of code, and the refactoring means involved are different.

Program Block refactoring

Program block refactoring refers to code snippet refactoring within each case. More than 80 refactoring methods are summarized in Martin Fowler's refactoring-improving the design of existing codes (electronic version). Examples of each technique are given in the book, as well as examples and further introductions in other languages. Because there are a lot of examples, so this article no longer give examples of these methods, interested students can learn through the above several ways. However, some of these techniques are improving the readability of the code, and some are improving the extensibility of the code, not every technology can effectively reduce cyclomatic complexity. There are several ways to reduce the cyclomatic complexity:

    • Refinement Function (Extract method). You have a piece of code that can be organized together and come out independently. Put this code in a separate function and explain the function's purpose.
    • decomposition Condition expression (decompose Conditional). You have a complex conditional (IF-THEN-ELSE) statement. Separate functions are extracted from the IF, then, else three-part paragraphs.
    • merges a conditional expression (Consolidate Conditional expression). You have a series of conditional tests that all get the same results. Combine these tests into a single conditional expression and refine the conditional expression into an independent function.
    • Merge Duplicate conditional fragments (Consolidate Duplicate Conditional fragments). You have the same piece of code on each branch of the conditional expression. Move the duplicated code outside of the conditional expression.
    • Remove the control flag (remove command flag). In a series of Boolean expressions, a variable has the effect of a "control tag." Replace the control token with a break statement or a return statement.

In addition to reducing cyclomatic complexity, these refactoring methods have the following benefits:

    • Satisfy the principle of single responsibility design, improve the readability of code.
    • Remove duplicate redundant code. You can delete a large number of the same conditional statements.
    • Meet the "tell, Don ' t Ask" principle, telling the object what to do, not how to do it.
Case refactoring

For a switch with dozens of cases, the cyclomatic complexity is often hundreds, and the program block refactoring obviously cannot solve its essential complexity. If you want to reduce its cyclomatic complexity, you will need to redesign your code.

The essence of switch/case language characteristic of C language is to describe a kind of table-checking logic, in which the table structure and the control of table are expressed by software. The table is described by code, which is obviously not the best way to implement it. What we need to do is to avoid the complexity of control, to focus on the organization of the data, to reflect the true structure of the simulated world, and to separate the data from the control.

The design of a table consists of two parts: the abstraction of an object (table item) and the construction of a table. How to abstract objects, how to divide the granularity of objects, how to design the relationship between objects? These problems are related to the training of abstract thinking ability, but also to the specific business logic, not the focus of this article. Readers can read the structure and interpretation of computer programs to learn more about software abstraction and other related technical details.

The construction method of table is the focus of this paper, which can be divided into compile period construction, link period construction and runtime construction. 3 methods have their own strengths and weaknesses, can be selected according to their own needs.

Compile time table build issue background

Boot boot supports 3 startup modes, and the user menu process for each startup mode is different. The boot menu supports input checking, storage, menu fallback, and so on. The original design of the function design is bloated, menu items through the switch/case to select processing, there are more than 10 function ring complexity of more than 40, the largest cyclomatic complexity of 147, code maintenance difficult.

Refactoring method

The boot start User menu is essentially a priority state machine, and each menu item is one of the states. Abstract menu item Object T_PROMT, which contains members such as prompt printing, input checking, storage, status jump, and so on. Build t_promt apromtarray[] Menu table describes all the menu item objects, through the MENUFSM implementation of state machine control: Through the object T_PROMT Jumpto interface to achieve the state of the jump, through the check interface to implement input inspection, Through the SetValue interface to implement the storage, through the parent implementation menu fallback to the parent menu (because the parent menu is dynamically variable, cannot be statically initialized, so in Jumpto dynamic assignment). The sample code is as follows:

typedef struct PROMPT {WORD32 type; Char *name;/*env name*/char *prompt;/*prompt info to user*/WORD32 (*check) (char *src);/*check func for user ' s INP    ut*/struct prompt* (*jumpto) (struct prompt*, WORD32);    struct prompt *parent; VOID (*setvalue) (CHAR *name);} T_promt;static T_PROMT apromtarray[] = {/* env name prompt string check func jump func pa Rent set Func */{type_normal, env_local_ip, "LOCAL IP:", Checkipaddr, Localipjump, NULL, Setcltipa  DDR}, {type_normal, env_server_ip, "SERVER IP:", Checkipaddr, Serveripjump, NULL, setseripaddr},/* Total    22 table entries, the following slightly */};static SWORD32 menufsm (struct prompt *menu) {SWORD32 dwret = BSP_OK;    WORD32 dwindex;            while (menu! = NULL) {if (menu = = Getprompt (env_null)) {dwret = Mode_menu_back;        Break        } dwindex= printpromptandgetuserinput (menu); if (dwindex! = normal_menu_back) {menu = Menu->jumpto (menu, dwindex);         } else {menu = menu->parent; }} return dwret;}    static struct prompt* getprompt (char *name) {WORD32 i = 0;    struct prompt *pt = NULL;    WORD32 dwsize = sizeof (Apromtarray)/sizeof (apromtarray[0]); for (i = 0; i < dwsize; i++) {if (strcmp (name, apromtarray[i].name) = = 0) {pt = &apromtarray[i]            ;        Break }} return pt;}

  

Run-time table build issue background

Kernel module through the IOCTL to the external interface, and this module IOCTL control code has 84, the original IOCTL function through the switch/case to complete the distribution and processing of the IOCTL, which resulted in a function code length of 767 lines, cyclomatic complexity of 124, difficult to maintain, Project software quality requirements are not met (function ring complexity is below 12).

Refactoring method

The abstract IOCTL interface object is ctrl_operations and instantiated, and the Bsp_iocmds_init is constructed by constructing a dictionary (hash table), which realizes the mapping of the IOCTL control code to the IOCTL interface, and the initialization of the hash table in the initialization of Board_dev_init module. In Boardctrl_do_ioctl, the processing interface of the IOCTL control code is obtained through the Hachicha table interface Bsp_dict_get.

Sample code

struct Ctrl_operations {SWORD32 (*board_init) (struct board *BD);    SWORD32 (*board_exit) (struct board *BD);/* A total of 92 table entries, the following slightly */}; struct Ctrl_operations Ioctl_ops = {. Inherits = &extern_ops,. epld_op = Bsp_epld_op,. E PLDRW = bsp_epld_rw,/* A total of 84 fields, the following slightly */};void bsp_iocmds_init (struct board *bd, pt_bsp_dict pdict) {bsp_dict    _add (Pdict, BSP_IOCMD_ROV_WR, BD-&GT;OPS-&GT;ROV_WR); Bsp_dict_add (Pdict, Bsp_iocmd_tcam_info, bd->ops->tcam_info);/* A total of 84 keys, the following slightly */}static SWORD32 __init board_dev_ init (void) {struct board *BD = Get_board ();/* Remove Extraneous code */Bd->iocmds = Bsp_dict_new (Dict_hint, bsp_cmp, Bsp_has    h);    Bsp_iocmds_init (BD, BD-&GT;IOCMDS); return BSP_OK;}    WORD32 boardctrl_do_ioctl (unsigned int cmd, void *pparam) {WORD32 dwionum = _ioc_nr (cmd);    struct board *BD = Get_board ();    WORD32 dwret = Bsp_e_brdctrl_notsupport;    Pt_ops_func OPS;    OPS = Bsp_dict_get (Bd->iocmds, dwionum); if (likely (OPS)) {       Dwret = OPS (BD, pparam); } return dwret;}

Of course, in addition to using a hash table, you can organize your data using data structures such as linked lists.

Link Period Table build issue background

Compile time table build and run time table build 2 methods, can optimize design, reduce cyclomatic complexity, but one thing is not perfect: When you add a table entry, you must modify the public static table (compile-time table build, if you need to modify Apromtarray) or register the function (run-time table build, if you need to modify BSP_ Iocmds_init), unable to fully meet the "development of the closure principle."

The Link Period table construction method can solve this problem.

Refactoring method

All (IOCTL control code, interface) data pairs (that is, tuples) are defined in the same section data segment through the GCC sections property. During the link phase, the linker constructs the initialization of this section data segment, in other words, the connector helps us to initialize and construct this object array. Then, using the __start_ctrl_op_section and __stop_ctrl_op_section symbols exported by GCC, Boardctrl_do_ioctl can complete the table-checking operation of the section data table.

This technique is used extensively in U-boot, Linux kernel. When adding a new table entry, you only need to add a ctrl_op_init, and you do not need to modify any common code or data.

Example code:

typedef void (*CTRL_OP) (struct board *bd), #define _init __attribute__ ((Section ("Ctrl_op_section")) #define Ctrl_op_ Init (num, func) ctrl_op __no_# #func _init = (ctrl_op) num;                                 Ctrl_op __fn_# #func _init = Funcextern ctrl_op __start_ctrl_op_section;extern ctrl_op __stop_ctrl_op_section;ctrl_op_ Init (BSP_IOCMD_ROV_WR, BSP_ROV_WR); Ctrl_op_init (Bsp_iocmd_tcam_info, bsp_tcam_info);/* Total 84 ctrl_op_init, below */ WORD32 boardctrl_do_ioctl (unsigned int cmd, void *pparam) {    WORD32 dwionum  = _ioc_nr (cmd);    struct board *BD = Get_board ();    WORD32 dwret = bsp_e_brdctrl_notsupport;    Ctrl_op * ptr = &__start_ctrl_op_section;        Do {        if ((WORD32) *ptr = = dwionum) {            ptr++;            if (likely (PTR))                return PTR (BD, pparam);        }        PTR + = 2;    } while (PTR < &__stop_ctrl_op_section);    return dwret;}

  

C language switch/case cyclomatic complexity optimization refactoring

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.