Synchronizing Declarations and Definitions of static Functions
Some organizations have interior standard for formatting source codes in the C programming language. In particular, we also have requirements for the code.
I already had the text Synchronization of the order of declarations and definitions of global functions. However, a similar story is also relevant for static functions. Among 463x We have internal code style rules and such mandatory code formatting requirement:
The sequence of static function declarations must match the sequence of static function definitions at the end of the *.c file.
I think it's clear to everyone that if static C functions were defined at the beginning of a C file, this desperate rule would not exist at all. But this is not the place for you! We also have another mandatory rule
All static functions must be defined at the very bottom of the *.c file.
In this regard, in order for the code to be compiled at all, it is now necessary to artificially re-declare the prototypes of these same static functions at the beginning of the *.c file. Isn't that normal? And if so, then let the order match. A circus with horses, and nothing more.
This is a prime example of how, literally out of thin air, you can create spokes in the wheels of yourself and your colleagues for many, many years to come. This is a classic example of IT drill that is not justified in any way.
It is clear that the behavior of the code will be one and the samethat static prototypes are sorted, that they are not sorted, it will not change the weather. The compiler does not care. Unit tests (if any) will pass the same way in any case.
You might think that we have nothing better to do here than shuffle function definitions around, tie some bows on them, instead of real work.
We don't have a single unit test. We compile the code in an Arduino-like IDE, we don't have UART-CLI. But we do make sure that the order of static function declarations matches the order of static function definitions at the end of the *.c file. How is that?
How do they say: “One zero in favor of Biden”?
However, there is such a rule here, and in order for commits with code to pass the censorship on Gerrit and end up in the common fund, no matter how you look at it, this requirement must be met.
What's the problem?
The problem is also that the intellectual employee who dreamed up all these rules was too lazy to scribble down utilities for automatic control of all this lawmaking. Probably, if supervisory utilities were developed, his ardor would quickly fade.
However, the rule mandatory and, no matter how you look at it, you have to live with it.
Any development begins only when full-fledged debugging tools appear.
It is obvious that manually tracking down these violations of the order of declarations and definitions is very tedious, routinely long and expensive. Therefore, programmers have long cherished the dream of the appearance of a magic wand: a console program that automatically finds these notorious violations of the order of static declarations.
And, as usual, such a utility in the public domain did not exist in nature until today, just as pure plutonium did not exist in the earth's crust before the appearance of the first man-made nuclear reactors.
That's why I wrote such a locator utility. I called it prototype_check with key csp. I will send the utility to all my fellow sufferers.
What software do you need?
# | Utility name | What is this utility for? |
1 | awk / gawk | csv string parser and processor |
2 | winmerge | Comparing text files |
3 | sed | deleting and replacing text in a file |
4* | ctags | extractor of text tokens from sources in different programming languages |
5 | cmp | file comparison utility |
What's the plan?
I propose to implement such a software conveyor. Only 4 stages. The scope of work is as follows:
Implementation
The core of this whole solution is an old utility ctags. ctags plays a major role here. Let's explain the options of the ctags utility that we will need in this solution here and now.
ctags option | Transcript |
-x –c-types=f | extract only functions from *.c file |
-x –c-kinds=p | extract function prototypes from *.c file |
–sort=no | do not sort found tokens (functions) in the report |
-fcTagReport.txt | save report to file сTagReport.txt |
And now an explanation of the utility's algorithm. So, let's start from scratch…
Phase 1: Show all functions in *.c file
To show only the functions you need to write
ctags.exe --sort=no -fcTagReport.txt -x --c-types=f i2c_drv.c
This raw log crystallized
Hidden text
I2C_Init function 783 C:\projects\source\I2C\i2c_drv.c STD_RESULT I2C_Init(void)
I2C_DeInit function 896 C:\projects\source\I2C\i2c_drv.c STD_RESULT I2C_DeInit(void)
I2C_StartReading function 968 C:\projects\source\I2C\i2c_drv.c STD_RESULT I2C_StartReading(const U8 nBus,
I2C_HighLevel_RX_ISR function 1758 C:\projects\source\I2C\i2c_drv.c void I2C_HighLevel_RX_ISR(const U8 nBus)
I2C_HighLevel_TX_ISR function 1775 C:\projects\source\I2C\i2c_drv.c void I2C_HighLevel_TX_ISR(const U8 nBus)
I2C_HighLevel_Error_ISR function 1932 C:\projects\source\I2C\i2c_drv.c void I2C_HighLevel_Error_ISR(const U8 nBus)
I2C_LL_GPIOInit function 2048 C:\projects\source\I2C\i2c_drv.c static void I2C_LL_GPIOInit(const U8 nArgBus)
I2C_LL_GPIODeInit function 2144 C:\projects\source\I2C\i2c_drv.c static void I2C_LL_GPIODeInit(const U8 nArgBus)
I2C_SetConfigTransfer function 2246 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_SetConfigTransfer(const I2C_BUS nArgBus,
I2C_SendData function 2417 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_SendData(const I2C_BUS nArgBus,
I2C_ReceiveData function 2470 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_ReceiveData(const I2C_BUS nArgBus,
I2C_ResetCtrl2Register function 2527 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_ResetCtrl2Register(const I2C_BUS nArgBus)
I2C_ClearFlags function 2593 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_ClearFlags(const I2C_BUS nArgBus,
I2C_CheckFlag function 2661 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_CheckFlag(const U8 nBus,
I2C_SetInterruptState function 2728 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_SetInterruptState(const U8 nArgBus,
I2C_RefreshTxdtRegister function 2783 C:\projects\source\I2C\i2c_drv.c static void I2C_RefreshTxdtRegister(I2C_tag* const pArgHw)
I2C_ConversionCfgIndex function 2810 C:\projects\source\I2C\i2c_drv.c static STD_RESULT I2C_ConversionCfgIndex(const U8 nArgBus,
Phase 2: Remove all non-static lines
In the ctags report, you need to leave only static functions. Or you need to delete all the lines that do not contain keyword static. This can be done with the console utility sed
sed -i '/ static /!d' cTagReport.txt
Only the definitions of static functions remain. This is the log we get
Hidden text
I2C_LL_GPIOInit function 2048 C:\projects\source\third_party\I2C\i2c_drv.c static void I2C_LL_GPIOInit(const U8 nArgBus)
I2C_LL_GPIODeInit function 2144 C:\projects\source\third_party\I2C\i2c_drv.c static void I2C_LL_GPIODeInit(const U8 nArgBus)
I2C_SetConfigTransfer function 2246 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_SetConfigTransfer(const I2C_BUS nArgBus,
I2C_SendData function 2417 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_SendData(const I2C_BUS nArgBus,
I2C_ReceiveData function 2470 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_ReceiveData(const I2C_BUS nArgBus,
I2C_ResetCtrl2Register function 2527 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_ResetCtrl2Register(const I2C_BUS nArgBus)
I2C_ClearFlags function 2593 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_ClearFlags(const I2C_BUS nArgBus,
I2C_CheckFlag function 2661 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_CheckFlag(const U8 nBus,
I2C_SetInterruptState function 2728 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_SetInterruptState(const U8 nArgBus,
I2C_RefreshTxdtRegister function 2783 C:\projects\source\third_party\I2C\i2c_drv.c static void I2C_RefreshTxdtRegister(I2C_tag* const pArgHw)
I2C_ConversionCfgIndex function 2810 C:\projects\source\third_party\I2C\i2c_drv.c static STD_RESULT I2C_ConversionCfgIndex(const U8 nArgBus,
Phase 3: Only the first column needs to be selected from the report
You can select columns from text files using the utility awk. With such a team.
gawk '{print $1}' cTagReport.txt > definitions.txt
Phase 4: Extract function prototypes
To extract only prototypes from src.c to cTagReport.txt, you need to run the following command
ctags --sort=no -fcTagReport.txt -x --c-kinds=p src.c
You will get a file like this with a list of prototypes.
Hidden text
I2C_LL_GPIOInit prototype 719 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static void I2C_LL_GPIOInit(const U8 nArgBus);
I2C_LL_GPIODeInit prototype 722 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static void I2C_LL_GPIODeInit(const U8 nArgBus);
I2C_SetConfigTransfer prototype 725 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_SetConfigTransfer(const I2C_BUS nArgBus,
I2C_SendData prototype 730 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_SendData(const I2C_BUS nArgBus,
I2C_ReceiveData prototype 734 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_ReceiveData(const I2C_BUS nArgBus,
I2C_ResetCtrl2Register prototype 738 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_ResetCtrl2Register(const I2C_BUS nArgBus);
I2C_ClearFlags prototype 741 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_ClearFlags(const I2C_BUS nArgBus,
I2C_CheckFlag prototype 745 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_CheckFlag(const U8 nBus,
I2C_SetInterruptState prototype 750 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_SetInterruptState(const U8 nArgBus,
I2C_RefreshTxdtRegister prototype 755 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static void I2C_RefreshTxdtRegister(I2C_tag* const pArgHw);
I2C_ConversionCfgIndex prototype 758 C:\projects\code_base_firmware\source\I2C\i2c_drv.c static STD_RESULT I2C_ConversionCfgIndex(const U8 nArgBus,
Phase 5: Extract only function names from the prototype report
Now you only need to select the function names. This is essentially the first column. Such tasks are solved by the cult utility awk
gawk '{print $1}' cTagReport.txt > declarations.txt
Inside the text file declarations.txt, the list of functions has crystallized as it is presented in the part where the static function prototypes are declared.
Phase 6: Compare feature lists
At this stage, the task was reduced to a simple comparison of two text files. In theory, they should be identical, like two peas in a pod. You can compare text files with the utility cmp
cmp -s declarations.txt definitions.txt
The result of the comparison will show the anomalies.
Here is the full script that does the check
cls
echo off
set srcFile=some_driver.c
set cTagFile=cTagReport.txt
set StativRepDef=StativRepDef.txt
set StativRepDec=StativRepDec.txt
rm -f %cTagFile%
rm -f %StativRepDef%
rm -f %StativRepDec%
set options=--sort=no
set options=%options% -f%cTagFile%
set options=%options% -x --c-types=f
ctags.exe %options% %srcFile%
sed -i '/ static /!d' %cTagFile%
gawk '{print $1}' %cTagFile% > %StativRepDef%
rm -f %cTagFile%
set options=--sort=no
set options=%options% -f%cTagFile%
set options=%options% -x --c-kinds=p
ctags %options% %srcFile%
gawk '{print $1}' %cTagFile% > %StativRepDec%
cmp -s %StativRepDec% %StativRepDef%
echo errorlevel=%errorlevel%
if "%errorlevel%"=="0" (echo same) else (echo diff)
Since the script is needed very often, I encapsulated it in a console utility prototype_check. This utility is some analogue of the utility BusyBox. Other utilities are called by the key. The script for checking static prototypes will be called by the key csp (check static prototype).
Debugging
This is what the utility log looks like if successful
And this is the log that will appear in case of negative success
The prototype_check utility performs a thorough check and creates two text files to see the defect. Shows which static functions are in the wrong place. Tool(a) actually found a desynchronization in the definitions. This is clearly visible with the WinMerge utility.
Thus, the programmer receives target indication of those static functions that need to be moved one way or another. Now it is clear what to do.
Results
In the end, I managed to develop a unique utility-supervisor, which, like a litmus test, automatically shows the loss of synchronization between the order of announcement and the order of definition static functions in a *.c file in the C programming language.
I love it, brothers, I love it.
It's a pleasure to live, brothers.
With our utility you don't have to worry
I hope the utility prototype_check or cmd script will help other microcontroller programmers to also increase the efficiency of their work in the matter of bringing the code to internal design standards.
If you need this utility, please contact us.
Dictionary
acronym | Transcript |
CSV | Comma-separated values |
csp | Check Static Prototyping |
Links