Пролог

Настал тот первый день, когда в программировании микроконтроллеров наконец пригодилась такая абстрактная структура данных как LIFO. Он же стек. Он же магазинная память.

На бытовом уровне все мы так или иначе имели дело со стеком. Стек это, по сути, стопка игральных карт на столе, патроны в магазине штурмовой винтовки AK-47, стопка тарелок на кухне, свежеиспечённые блинчики на тарелке, стопка книг на столе, RAM память для локальных переменных внутри компьютерных программ тоже растет и уменьшается по правилу LIFO, говорят про стек протоколов в модели ISO-7. Синтаксический разбор XML файла требует работы LIFO. Даже детская дошкольная игрушка Ханойская башня - это тоже LIFO.

Всё это примеры стека (LIFO). Это когда брать и класть "чиво-либо" можно только с одной стороны.

Сейчас объясню при каких именно обстоятельствах мне понадобился стек на работе ...

У нас в организации существует обязательное внутреннее требование к оформлению исходных текстов программ на языке программирования Си для микроконтроллеров, которое звучит так:

блок кода xxx() {} должен заканчиваться комментарием "end of ...." (см. шаблон)

В переводе на кухонный язык это значит, что в конце каждого блока if(...) {...} ; switch(...) {...} ; for(...) {...} и т.п. необходимо пиcать комментарий

// end of if(...). end of switch(...) end of for(...) соответственно.

Правило обязательно к применению, если блок с фигурными скобками охватывает больше чем 4 строчки кода, а для функций правило обязательно во всех случаях.

Вот так выглядит это художество в образцовом куске кода: Это тот самый "см. шаблон", который надо как попугай всегда и везде повторять.


//**************************************************************************************************
//! [Description of MODULE_FunctionTwo]
//!
//! \note       [text]
//!
//! \param[in]  parameterZero - [description of parameterZero]
//! \param[in]  parameterOne - [description of parameterOne]
//!
//! \return     [Description of return value]
//**************************************************************************************************
static DATA_TYPE MODULE_FunctionTwo(DATA_TYPE parameterZero,
                                    DATA_TYPE parameterOne)
{
    DATA_TYPE returnValue;
    
    // [Description...]
    switch (expression)
    {
        case CASE_ONE:
            caseOneCnt++;
            break;

        case CASE_TWO:
            caseTwoCnt++;
            break;

        default:
            caseDefaultCnt++;
            break;
    } // end of switch (expression)
    
    return returnValue;
} // end of MODULE_FunctionTwo()

Как можно заметить, тут после switch присутствует комментарий // end of switch (expression). И в конце имени функции тоже // end of MODULE_FunctionTwo().

У нас на цензуре в Gerrit коммит просто не примут в ветку main, если хотя бы в одном месте нет этого комментария // end of xxx(). Для этого у нас в организации есть специальный надзиратель - апологет именно правила end of xxx(), который даже не глядя на функционал программного компонента и модульные тесты (про которые он, к слову, даже ничего не слышал) просто банит коммиты, если хотя бы в одном месте нет этого текстового комментария // end of xxx(...) .

А теперь внимание...

Сам программист-апологет требования комментариев // end of xxx() это требование в своих исходниках игнорирует!

По прошлому тексту многие не верили, что такое вообще возможно. Вот я в качестве доказательства выкладываю скриншот.

Надзиратель обнаружил отсутствующий комментарий после закрывающейся фигурной скобки
Надзиратель обнаружил отсутствующий комментарий после закрывающейся фигурной скобки

Вот так... Правила, да не для всех оказывается...

Даже язык не поворачивается назвать такие порядки словом "инспекция программ". Это самая настоящая цензура.

Вот такие пирожки с капустой... Понимаете? А мы с этим живем...

Понятное дело, что эти комментарии компилятору, да и для модульных тестов функционала нужны, как собаке бензобак.

Однако, требование такое есть. Раз надо, так надо...

В чём проблема?

Проблема в том, что вручную прослеживать везде исполнение этого нелепого правила очевидно очень утомительно. Особенно в файлах, где уже 5000-7000+ строк кода.

Поэтому, как ни крути, тут нужна волшебная палочка - консольная утилита-локатор, которая сама будет находить все места, где отсутствуют комментарии // end of xxx() после закрывающейся фигурной скобочки }.

При этом за 30 лет существования этого правила в этой организации никто из программистов эту утилиту тут так и не написал. За 30 лет! Да, господа... Вот так...

Этой утилиты не существовало в природе до сегодняшнего дня. Запомним этот день!

Любая разработка начинается только тогда, когда появляются полноценные средства для отладки. Подобно тому как альпинизм начинается с верёвок.

Реализация

Текстовое описание алгоритма

Решить эту задачу можно при помощи такой классической структуры данных как LIFO (стек). Идея в следующем.

Открыть *.c файл и читать его строчка за строчкой и символ за символом. Как только встретится символ { положить в стек структуру, которая запомнит номер строки и тип скобки. Когда встретиться } тоже запомнить в стек структуру с информацией про скобку.

Переменная

Возможные значение

1

тип скобки

{ или }

2

номер строки

натуральное число

После каждого добавления в стек скобки } следует проверять можно ли сократить скобки. Если предпоследний элемент в стеке это {, а последний элемент в стеке это }, то можно сократить.

В этом случае мы извлекаем два последние элемента из LIFO. Вычисляем разницу номеров строк. Если значение больше 4 - проверяем есть ли комментарий // end of xxx(). Если есть, то идем дальше. Если нет, то выдаём красное сообщение ошибки, что на строке L отсутствует комментарий // end of xxx() и увеличиваем счетчик ошибок.

Если видит }, или }; то пропускать такие строчки. Это не конец функции или оператора, а конец структуры или массива.

Вот так просто и не затейливо. Между делом, утилита ещё и проверяет баланс открытых и закрытых фигурных скобочек. И так до конца *.c файла.

При этом утилиту я написал на Си буквально за два вечера и ядро функционала составило менее чем 250 строк.

ядро утилиты
#include "end_of_block.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "log.h"
#include "file_pc.h"
#include "lifo_array.h"
#include "str_utils_ex.h"
#include "csv.h"


bool end_of_block_mcal_init(void) {
    bool res = true;
    log_level_get_set(LINE, LOG_LEVEL_INFO);
    log_level_get_set(END_OF_BLOCK, LOG_LEVEL_INFO);
    LOG_INFO(END_OF_BLOCK, "END_OF_BLOCK_VERSION:%u", END_OF_BLOCK_DRIVER_VERSION);
    return res;
}

static bool end_of_block_proc_brace_open(EndOfBlockHandle_t *const Node) {
    bool res = false;
    //push to stack
    BraceInfo_t* Brace = (BraceInfo_t*) malloc(sizeof(BraceInfo_t));
    if(Brace) {
        Brace->dir = BRACE_DIR_OPEN;
        Brace->line_number = Node->cur_line;
        Brace->code = END_OF_BLOCK_ID;

        Array_t Elem = {0};
        Elem.pArr = (uint8_t*)Brace;
        Elem.size = sizeof(BraceInfo_t);
        res = lifo_arr_push(&(Node->LifoArray), Elem);
        if(res){
            LOG_DEBUG(END_OF_BLOCK, "Line:  %7u  ,LifoArrayPush {",Node->cur_line);
        }else{
            LOG_ERROR(END_OF_BLOCK,"ErrPush");
        }
    }else{
        LOG_ERROR(END_OF_BLOCK,"ErrMalloc");
    }
    return res;
}

static bool EndOfBlockIsValidBrace(BraceInfo_t* Node){
    bool res = false;
    if(Node) {
        if((BRACE_DIR_CLOSE==Node->dir) || (BRACE_DIR_OPEN==Node->dir))
        {
            if(0<Node->line_number){
                if(END_OF_BLOCK_ID==Node->code){
                    res = true;
                }else{
                    LOG_ERROR(END_OF_BLOCK,"NoCodeID");
                }
            }else{
                LOG_ERROR(END_OF_BLOCK,"NotLine");
            }
        }else{
            LOG_ERROR(END_OF_BLOCK,"NotBrase");
        }
    }

    if(false==res){
        LOG_ERROR(END_OF_BLOCK,"%s",BraceInfoToStr(Node));
    }
    return res;
}

bool end_of_block_try_reduce(EndOfBlockHandle_t *const Node) {
    bool res = false;
    LOG_DEBUG(END_OF_BLOCK,  "TryReduce");
    Array_t PrevNode={0};
    res = lifo_arr_peek_num(&(Node->LifoArray),   0, &PrevNode);
    if(res) {
        res = LivoIsValidItem(&PrevNode);

        BraceInfo_t PrevBrace={0};
        PrevBrace =    *((BraceInfo_t*)     PrevNode.pArr);
        //memcpy((void *)&PrevBrace,(void *)PrevNode.pArr,sizeof(BraceInfo_t));
        //memcpy((void *)&PrevBrace,(void *)PrevNode.pArr,sizeof(BraceInfo_t));

         Array_t PrevPrevNode={0};
        res = lifo_arr_peek_num(&(Node->LifoArray),   1, &PrevPrevNode);
        if (res) {
            res = LivoIsValidItem(&PrevPrevNode);

            BraceInfo_t PrevPrevBrace;
            PrevPrevBrace =    *((BraceInfo_t*)     PrevPrevNode.pArr);
            //memcpy((void *)&PrevPrevBrace,(void *)PrevPrevNode.pArr,sizeof(BraceInfo_t));

            res = EndOfBlockIsValidBrace(&PrevBrace);
            res = EndOfBlockIsValidBrace(&PrevPrevBrace);

            if(BRACE_DIR_CLOSE==PrevBrace.dir) {
                if(BRACE_DIR_OPEN==PrevPrevBrace.dir) {
                    LOG_DEBUG(END_OF_BLOCK,  "Spot{} pair");
                    Node->pair_cnt++;
                    uint32_t line_diff = PrevBrace.line_number - PrevPrevBrace.line_number;
                    if (Node->line_threshold < line_diff) {
                        char* subStr = strstr(Node->curLine,"end of");
                        if(subStr) {
                            res = true;
                            Node->ok_counter++;
                        } else {
                            Node->violation_counter++;
                            LOG_ERROR(END_OF_BLOCK,"Err:%3u,%s:Line: %7u   ,lack[ // end of xxx() ]",
                                    Node->violation_counter,
                                    Node->fileShortName,
                                    PrevBrace.line_number
                                    );
                            res = true;
                        }
                    }
                    res = lifo_arr_delete_cnt(&(Node->LifoArray), 2) ;
                }
            }
        }else{
            LOG_ERROR(END_OF_BLOCK,"PeekErr");
        }
    }else{
        LOG_ERROR(END_OF_BLOCK,"PeekErr");
    }
    return res;
}

static bool end_of_block_proc_brace_close(EndOfBlockHandle_t *const Node) {
    bool res = false;
    //push to stack
    BraceInfo_t *Brace = (BraceInfo_t*) malloc(sizeof(BraceInfo_t));
    if(Brace) {
        Brace->dir = BRACE_DIR_CLOSE;
        Brace->line_number = Node->cur_line;
        Brace->code = END_OF_BLOCK_ID;

        Array_t Elem;
        Elem.pArr = (uint8_t*)Brace;
        Elem.size = sizeof(BraceInfo_t);

        res = lifo_arr_push(&(Node->LifoArray), Elem);
        if(res){
            LOG_DEBUG(END_OF_BLOCK,  "Line:%3u,LifoArrayPush }",Node->cur_line);
            res = end_of_block_try_reduce(Node);
        }else{
            LOG_ERROR(END_OF_BLOCK,"ErrPush");
        }
    }else{
        LOG_ERROR(END_OF_BLOCK,"ErrMalloc");
    }
    return res;
}

static bool end_of_block_proc_byte(EndOfBlockHandle_t* Node, char letter) {
    bool res = false ;
    switch(letter){
        case '{': {
            res = end_of_block_proc_brace_open(Node);
        } break;
        case '}': {
            res = end_of_block_proc_brace_close(Node);
            //try reduce
            res = true;
        } break;
        case '\n': {res = true;} break;
        case '\r': {res = true;} break;
        default: {res = true;} break;
    }
    return res;
}

static bool end_of_block_proc_line(EndOfBlockHandle_t* Node){
    bool res = true;
    uint32_t len=strlen(Node->curLine);
    uint32_t i = 0 ;
    uint32_t ok_cnt = 0 ;
    for(i=0;i<len;i++){
        res = end_of_block_proc_byte(Node, Node->curLine[i]);
        if (res) {
            ok_cnt++;
        } else {
            LOG_ERROR(END_OF_BLOCK, "ProcByteErr:[%c]",Node->curLine[i]);
        }
    }
    if(len==ok_cnt) {
        res = true;
    }else{
        LOG_ERROR(END_OF_BLOCK, "ProcLineErr:[%s]",Node->curLine);
        res = false;
    }
    return res;
}

bool end_of_block_check(const char *const file_name_c, uint32_t lines ) {
    bool res = false;
    if (file_name_c) {
        if( 0 < lines) {
            EndOfBlockHandle_t EndOfBlock={0};
            EndOfBlock.line_threshold = lines;
            res = file_pc_realpath(file_name_c, EndOfBlock.fileNameC);
            if(res) {
                Array_t LifoArray[800] = {0};
                res = csv_parse_last_text(EndOfBlock.fileNameC, '/',
                                          EndOfBlock.fileShortName,
                                          sizeof(EndOfBlock.fileShortName) );
                if(res) {
                    res = lifo_arr_init(&EndOfBlock.LifoArray, LifoArray, ARRAY_SIZE(LifoArray));
                    log_res(END_OF_BLOCK, res, "LiFoInit");
                    if(res) {
                        LOG_DEBUG(END_OF_BLOCK, "CheckEndOfBlockCommentIn:[%s]", EndOfBlock.fileNameC);
                        EndOfBlock.filePtr = fopen(EndOfBlock.fileNameC, "r");
                        if(EndOfBlock.filePtr) {
                            LOG_DEBUG(END_OF_BLOCK, "OpenOkFile:[%s]", EndOfBlock.fileNameC);
                            while(NULL != fgets(EndOfBlock.curLine, END_OF_BLOCK_MAX_LINE_SIZE, EndOfBlock.filePtr)) {
                                EndOfBlock.cur_line++;
                                LOG_PARN(END_OF_BLOCK, "%u,%s",EndOfBlock.cur_line, EndOfBlock.curLine);
                                res = end_of_block_proc_line(&EndOfBlock);
                                if(res) {
                                    EndOfBlock.ok_cnt++;
                                }else{
                                    EndOfBlock.err_cnt++;
                                }
                            }
                            fclose(EndOfBlock.filePtr);
                        }
                    }
                }

            }else{
                LOG_ERROR(END_OF_BLOCK, "OpenFileErr:[%s]", EndOfBlock.fileNameC);
            }

            if(0==EndOfBlock.err_cnt) {
                res = true;
            } else {
                LOG_ERROR(END_OF_BLOCK, "Err:%u", EndOfBlock.err_cnt);
            }

            LOG_INFO(END_OF_BLOCK, "%s", EndOfBlockNodeReportToStr(&EndOfBlock));
        }
    }
    return res;
}

Как можно заметить, эта утилита использует такие программные зависимости как LIFO, CSV, LOG, STRING и FILE. Подразумевается, что у Вас в репозитории уже присутствует реализация на Си этих программных компонентов SWC.

Однако в организации 600+ программистов за 30 лет никто даже этого не сделал. Это как?

Отладка

Мне удалось написать на Си консольную утилиту, которая находит в Си-коде все места, где отсутствуют комментарий после закрывающейся фигурной скобки }.

Взводится утилита следующим образом. Надо просто осуществить пуск *.bat файла с таким содержимым.

:: Windows cmd script
cls

set line_threshold=5
set file_name=C:/project/HAL/GPIO/gpio_drv.c 
code_style_check.exe eob %line_threshold% %file_name%
::  eob - end of block

Вот такой лог метаданных выдает утилита:

Вот и в другом файле с исходниками утилита обнаружила многочисленные нарушения нашего внутреннего code-style. Непорядок...

Благодаря этому логу можно смело откопать все места и ликвидировать ошибки, добавив там комментарии // end of xxx().

Дистрибутив утилиты

Если вы являетесь коллегой по несчастью, то можете тоже взять у меня готовую утилиту code_style_check.exe для решения задачи поиска отсутствующих комментариев.

Скачать tool(у) можно у меня с github

Итог

Удалось автоматизировать процесс проверки правила с порядковым номером №456 нашего внутреннего code-style. Да, господа, у нас более четырех сотен обязательных правил в компанейском code-style...

Добиться этого удалось при помощи отдельной специально разработанной консольной радар-утилиты целеуказания. Утилита автоматически локально выявит недочеты в коде ещё до комита в общак.

Надеюсь, этот текст и выложенная в открытый доступ утилита помогут Вам в прохождении цензуры при разработке программ для микроконтроллеров в России.

Словарь

Акроним

Расшифровка

SWC

SoftWare Component

LIFO

last-in-first-out

ISO

International Organization for Standardization

CSV

Comma-separated values

Ссылки

У меня присутствуют утилиты для выявления и других правил нашего внутреннего code-style. Про них можно почитать тут

Название текста

URL

1

Стилистический анализатор: синхронизация объявлений и определений static функций

https://habr.com/ru/articles/846020/

2

Стилистический Анализатор: Синхронизация порядка объявлений и определений функций

https://habr.com/ru/articles/844436/

3

Интеграция Стилистического Анализа в общий Make Скрипт Сборки Проекта

https://habr.com/ru/articles/843746/

4

Нельзя Просто Так Пойти и Купить Овцу (или Потёмкинская Деревня в Коде)

https://habr.com/ru/articles/837396/

5

Интеграция clang-format в Процесс Сборки

https://habr.com/ru/articles/833500/

6

Почему Сборка с Помощью Есlipse ARM GCC Плагинов это Тупиковый Путь

https://habr.com/ru/articles/794206/

7

Дистрибутив утилиты code_style_check.exe

https://github.com/aabzel/Artifacts/tree/main/end_of_xxx

8

Синтаксический разбор CSV строчек

https://habr.com/ru/articles/765066/

Вопросы

1
-- Зачем в конце if(...) {} писать if(...) {} // end of if(...)?

Комментарии (92)


  1. vadimr
    12.12.2024 16:07

    По-хорошему надо ещё лексический контекст учитывать.

    Если в вашем исходнике поменять местами case '{' и case '}' и подать его на вход самому себе, то ему не захорошеет (хотя он и так нарушает проверяемое им же самим правило).

    А если совсем строго, то надо раскрывать макроподстановки.

    Вопросы--Зачем в конце if(...) {} писать if(...) {} // end of if(...)

    Фортран своими руками.


    1. Jijiki
      12.12.2024 16:07

      поидее если считаем { +1 а } -1(где 1 - это 2 пробела допустим), но если учесть функционал что просто скобочку на новую строку поставить(если открывающая скобочка в конце строки ) и попутно удаляя табы перед строкой и в конце строки, а код при этом компилируется, то поидее не плохой форматер выходит, а так да надо поидее все токены ловить и по новой простраивать как я понимаю по хорошему


    1. RomanDrDev
      12.12.2024 16:07

      Хороший совет.

      А еще я при парсинге файлов конфигурации 1С (которые в SQL в dbo.Config лежат, а в файловых базах в таблицах .dt файла) сталкивался с ситуацией, когда в комментариях к реквизиту/объекту была открывающая фигурная скобка, но не было закрывающей. И это тоже ломало CLR сборку, пока не добавил в нее обработку комментариев.


    1. aamonster
      12.12.2024 16:07

      Не надо раскрывать макроподстановки. Если макросы нарушают баланс скобок или делают ещё что-то, что помешает работе такого анализатора – то они помешают и человеку читать код, а значит, должны быть переписаны.


      1. vadimr
        12.12.2024 16:07

        Допустим, могут быть через макросы определены свои операторные скобки для какой-то цели. Типа, начало какого-то контекста и конец контекста. Первый макрос будет включать {, а второй - }.


        1. aamonster
          12.12.2024 16:07

          Соответственно, эти макросы всегда будут идти парами, и баланс скобок не нарушится. Приемлемо.


          1. vadimr
            12.12.2024 16:07

            Если в программе нет ошибок.


            1. aamonster
              12.12.2024 16:07

              Да. Причём грубых, которые можно обнаружить автоматически.


  1. FilimoniC
    12.12.2024 16:07

    В программировании микроконтроллеров, кажется лишним закрывать блоки комментариями, т.к. внутри этого блока можно легко встретить оптимизационную дичь вроде goto-инструкций.


    1. YDR
      12.12.2024 16:07

      я бы не стал использовать goto даже в этом случае. Не сильно то оно и оптимизирует по сравнению с нормальным стилем.


      1. vadimr
        12.12.2024 16:07

        Всё зависит от характера кода. В продвинутой работе с железом без go to сложно обойтись, так как логика выполнения определяется фактическими событиями в аппаратуре, а не паттернами проектирования. А высокоуровневые абстракции для такой модели очень дороги.


  1. vindy
    12.12.2024 16:07

    А Можно Ваш Стилистический-Анализатор Как-то Приспособить К Правке Заголовков Статей?


    1. marapper
      12.12.2024 16:07

      ССАПНКФС... возможно, автор нам хочет что-то сказать.


  1. Jijiki
    12.12.2024 16:07

    тема классная, я форматер писал для интереса (парсер + форматер - как раз по этим скобочкам ориентировался)


  1. randomsimplenumber
    12.12.2024 16:07

    Если бы мне вдруг понадобилось решать подобную задачу - моя программа сама бы расставляла правильные комментарии.


    1. aabzel Автор
      12.12.2024 16:07

      Если бы мне вдруг понадобилось решать подобную задачу - моя программа сама бы расставляла правильные комментарии.

      То, что мы сейчас прочитали - это именно то, что нужно для решения всей проблемы.

      Предлагаю Вам @randomsimplenumber до конца месяца написать, эту утилиту и написать тут на habr пояснительную записку про неё.

      Чтобы уже в ближайшее время можно было автоматически проставлять корректные комментарии // end of xxx(...) после закрывающихся фигурных скобок во всем *.c файле.


      1. randomsimplenumber
        12.12.2024 16:07

        Если бы мне вдруг понадобилось ;)

        Ключевое слово выделено жирным.


      1. aamonster
        12.12.2024 16:07

        Сколько платите за решение?


        1. aabzel Автор
          12.12.2024 16:07

          Вы сделайте утилиту и укажите реквизиты для донатов. Так и делают сейчас тулы.


          1. aamonster
            12.12.2024 16:07

            Вариант, конечно, но не вижу рынка для конкретно такой тулзы за пределами вашей организации. А более продвинутый линтер – это уже серьёзный проект, не на день-другой.


            1. aabzel Автор
              12.12.2024 16:07

              Что получается - это только у нас подписывают комментариями закрывающиеся фигурные скобки?


              1. randomsimplenumber
                12.12.2024 16:07

                В каждой избушке свои погремушки;)


              1. aamonster
                12.12.2024 16:07

                Ну, мне казалось, вы про это и говорили :-)

                У нас #else/#endif принято подписывать, и мне это нравится.


                1. randomsimplenumber
                  12.12.2024 16:07

                  Это как раз тот тот случай, когда подписывать полезно.


    1. aamonster
      12.12.2024 16:07

      Угу. Для линтеров такая возможность – база. А ещё удобнее плагин к используемой IDE, который подсказывает эти подстановки.


  1. serchu
    12.12.2024 16:07

    А можно узнать какую проблему решает это правило? Не имел опыта с программированием микроконтроллеров, возможно, поэтому правило выглядит надуманным


    1. aabzel Автор
      12.12.2024 16:07

       какую проблему решает это правило?  .... правило выглядит надуманным

      Вот именно, дружище, что это правило никакой реальной проблемы не решает.

      Вы правы. Правило комментария после } - это самое настоящее надуманное правило.

      У нас в организации таких правило больше 400+ и у нас регулярно в чатах люди жалуются на то, что внутренний код-стайл давно доведён до абсурда.


      1. brumbrum
        12.12.2024 16:07

        Такую проблему стоит решать иначе — уходить из этой организации.


        1. aabzel Автор
          12.12.2024 16:07

          На самом деле это даже здорово придумывать всё новые и новые требования к code style. Так можно аргументировать раздувание фронта работ начальству, что тебе и коллегам гарантирована оплачиваемая работа на обозримую перспективу. Можно годами как пиявка высасывать из компании зарплату за то, что с меньшими требованиями к code style можно сделать максимум за 2-3 месяца


          1. randomsimplenumber
            12.12.2024 16:07

            На самом деле это даже здорово

            Ну, если всё всех устраивает - в чем проблема то? Автоматических средств проверки ваших 400 правил хорошего тона нет, цензурится вручную, но не очень строго. Написание 400 костылей займёт 400 * 2 дня = почти 3 года. И то, за это время могут ещё правил придумать. Если уж пилить - то что-то новое для себя, а не повторение темы, которую проходили когда то на 2 курсе. AI модная тема, flex - хз что это но выглядит интересно, ну и прочее ненормальное программирование ;)


            1. aabzel Автор
              12.12.2024 16:07

              Ну, если всё всех устраивает - в чем проблема то? 

              Прость за державу обидно.
              Вместо развития техники и технологий как в 196x, теперь мы в 202x занимаемся IT-муштрой.


        1. aabzel Автор
          12.12.2024 16:07

          Зачем уходить ,если за код стайл тут платят зарплату? И не маленькую...

          При этом писать комменты это много проще, чем чинить чужие баги на новой работе.


          1. randomsimplenumber
            12.12.2024 16:07

            Зачем уходить ,если за код стайл тут платят зарплату?

            За державу обидно (ц), но 20 баксов это 20 баксов (тоже ц) ;) Налицо дилемма вагонетки трусов и крестика.

            Ну, кстати, вот что происходит, когда на программиста начинают натягивать странные KPI.


    1. aabzel Автор
      12.12.2024 16:07

      правило выглядит надуманным

      Вот, можете ознакомиться с полным нашим парадом абсурда
      Потёмкинская Деревня в Коде

      https://habr.com/ru/articles/837396/


      1. randomsimplenumber
        12.12.2024 16:07

        А у вас вся существующая кодовая база удовлетворяет этим требованиям?

        А если кто-то вносит правки в условие - текст в комментариях тоже исправляет?


        1. aabzel Автор
          12.12.2024 16:07

          И да и нет.

          Одним можно делать существенные отступления от существующего код-стайл.

          По фамилиям эти одни в компании - родственники руководству.

          Остальным нет. Остальных гоняют по IT-муштре по-полной.


  1. aeder
    12.12.2024 16:07

    А использовать проверенную десятилетиями связку flex+bison было слишком страшно?

    Это ведь идеальная задача для парсера и грамматики.

    Учитывая, что судя по вашему описанию - ваша утилита будет глючить на

    • комментариях, содержащих фигурные скобки

    • строковых литералах, содержащих фигурные скобки

    • символьных литералах, содержащих фигурные скобки

    В общем, советую переделать по-взрослому.

    Кстати, если немного подумаете - то сможете не просто проверочную утилиту реализовать, а реализовать утилиту, которая автоматом будет вставлять эти комментарии.


    1. aabzel Автор
      12.12.2024 16:07

      То, что мы сейчас прочитали - это именно то, что нужно для решения всей проблемы.

      Предлагаю Вам @aeder до конца месяца написать, эту утилиту и написать тут на habr пояснительную записку про неё.

      Чтобы уже в ближайшее время можно было автоматически проставлять корректные комментарии // end of xxx(...) после закрывающихся фигурных скобок во всем *.c файле.


    1. aabzel Автор
      12.12.2024 16:07

      А использовать проверенную десятилетиями связку flex+bison было слишком страшно?

      Критикуешь- предлагай
      Предлагая - делай
      Делая - отвечай


      1. vindy
        12.12.2024 16:07

        Все три утверждения - манипулятивная стратегия защиты в дискуссии.


        1. aabzel Автор
          12.12.2024 16:07

          Вы хоть знаете кому принадлежат эти слова?


          1. IvaYan
            12.12.2024 16:07

            Ссылка на авторитет -- не менее манипулятивная стратегия. У вас нет аргументов в защиту вашего утверждения, так что вам не остается ничего, кроме как сослаться на кого-то, кто более уважаем, чем вы, в надежде, что это сделает вашу точку зрения весомее.


            1. aabzel Автор
              12.12.2024 16:07

              Просто развелось очень много, мягко говоря, Емеля-советников типа @aeder с нулевым количеством публикаций...


              1. randomsimplenumber
                12.12.2024 16:07

                Сперва добейся (ц)


          1. vindy
            12.12.2024 16:07

            Я не знаю, кому принадлежат эти слова. Так как услышал я их от вас, то буду благодарен, если вы сможете аргументировать, почему нельзя критиковать что-либо, не предлагая решения? Например, я ничего не понимаю в теплоснабжении жилых кварталов в своем городе, значит ли это, что я не должен критиковать коммунальную службу, которая меня в морозы оставила с холодными батареями на двое суток? Я совершенно искренне не знаю, что именно им нужно улучшить у себя, чтобы такое не происходило, я не теплотехник и не управленец. Мне точно из-за этого нужно молчать? Также, переходя к пункту 2 и 3 вашего утверждения, нужно ли мне молчать, если я не готов устранить эту аварию самостоятельно и взять на себя ответственность за качество ремонта?


  1. Timick
    12.12.2024 16:07

    Судя по всему элементарная задача по строковому интерпретатору формул (скобочки определяют порядок расчета). В 90-м решали такую на конкурсе Юный программист с использованием ПЭВМ Правец-8А


    1. aabzel Автор
      12.12.2024 16:07

      Вы писали интерпретатор python или M4.


    1. DrGluck07
      12.12.2024 16:07

      А мы Сокобан написали на Правец 8Д.


  1. CitizenOfDreams
    12.12.2024 16:07

    В конце каждого блока if(...) {...} ; switch(...) {...} ; for(...) {...} и т.п. необходимо пиcать комментарий // end of if(...). end of switch(...) end of for(...) соответственно.

    Тоже так делаю, потом удобнее смотреть, где конец чего. Но, конечно, в абсолют не возвожу.

    С другой стороны, я пишу сам для себя программы для мелких микроконтроллеров, вплоть до PIC10 (512 байт ПЗУ, 64 байта ОЗУ, 200-300 строк кода). Возможно, при совместной работе над более крупными проектами подобные правила имеют смысл, даже если кажутся бесполезными и драконовскими?


    1. aamonster
      12.12.2024 16:07

      Обычно если сходу по "ёлочке" не видно начало блока – надо рефакторить, вынося куски кода в отдельные функции.


      1. aabzel Автор
        12.12.2024 16:07

         вынося куски кода в отдельные функции.

        Да. Однако у нас в организации есть другое правило, которое блокирует этот процесс.

        "Порядок объявления функций должен совпадать с порядком определения функций."

        Это подрывное правило исключает возможность создания большого количества маленьких Cи-функций, которые помещаются на один экран.

        Дело в том что штатным программистам лень потом вручную сортировать функции по порядку объявления.
        Из-за этого правила программисты предпочитают создавать 3, максимум 5 - мега функций-богов по тысяче строк в каждой, которые делают всё.

        И это убивает модульность, читаемость, тесто пригодность и восприятие кода другими сотрудниками.

        Это самый настоящий anti-pattern программирования.

        Да, вот так, господа...


        1. randomsimplenumber
          12.12.2024 16:07

          Порядок объявления функций должен совпадать с порядком определения функций

          Дюра лекс конечно, но есть ли этому правилу непротиворечивое объяснение? Или это из тех домезозойских времён, когда текст программы нужно было распечатать на АЦПУ?


          1. aabzel Автор
            12.12.2024 16:07

            Уже обсуждали вот
            https://habr.com/ru/articles/844436/#comment_27317592

            Автор требования по ходу аутист. Он и в офисе себя ведет странно.
            К таким надо относиться с пониманием...


  1. nin-jin
    12.12.2024 16:07

    Один вместо использования инструментов, постоянно показывающих шапку блока на экране вводит правила по ее копированию в конец блока. Другой вместо написания тула автоматически добавляющего комментарии делает тул, который лишь показывает где их нет. И никого не смущают файлы на 5к строк и язык разработки из мезозоя. Я бы вас обоих даже джунами не взял, ибо тут наблюдается полная ментальная проф непригодность - программисты, не способные автоматизировать даже свою работу.


    1. aabzel Автор
      12.12.2024 16:07

        И язык разработки из мезозоя

      Вы, Дмитрий, не забывайте, что это хаб программирования микроконтроллеров. А производители микроконтроллеров сами рекомендуют именно си для разработки.


      1. nin-jin
        12.12.2024 16:07

        Ну так и используйте современную его версию: https://habr.com/ru/articles/511334/


        1. aabzel Автор
          12.12.2024 16:07

          Боюсь мировое сообщество embedded разработчиков Вас (как front-end программиста) не поймет.

          Если Вы не в курсе, то внутри микроконтроллеров ARM ядро. И для его запуска компания ARM дает CMSIS код, который написан на чистом Си.

          Вот предложите британскому ARMу переписать CMSIS на D.
          А потом напишите нам, что они Вам ответили.



          1. nin-jin
            12.12.2024 16:07

            Я смотрю Вы (как представитель embedded разработчиков) традиционно поленились прочитать статью, от чего и высказываете глупые гипотезы о необходимости переписывания стороннего кода.


    1. CitizenOfDreams
      12.12.2024 16:07

      и язык разработки из мезозоя

      Ну, в микроконтроллерах вариантов не особо много - или использовать язык из эпохи мезозоя, или для мигания светодиода придется ставить контроллер из эпохи Бака Роджерса.

      Скрытый текст


      1. DrGluck07
        12.12.2024 16:07

        А "ардуинщики" именно так и делают, ставят какой-нибудь F429 чтоб мигать тремя светодиодами по команде с UART на скорости 9600.


    1. aabzel Автор
      12.12.2024 16:07

      Один вместо использования инструментов, постоянно показывающих шапку блока на экране вводит правила по ее копированию в конец блока. 

      Дело в том что тот человек, который придумывает у нас правила кодстайла обладает слишком низкой квалификацией, чтобы разбираться в инструментах и тем более в их разработке


      1. CitizenOfDreams
        12.12.2024 16:07

        тот человек, который придумывает у нас правила кодстайла обладает слишком низкой квалификацией, чтобы разбираться в инструментах и тем более в их разработке

        Тогда возникают сомнения в квалификации человека, который поручил ему придумывать правила кодстайла. Мы же не позволяем людям, которые не разбираются в какой-то области деятельности, придумывать законы для нее? Wait...


    1. aabzel Автор
      12.12.2024 16:07

      Другой вместо написания тула автоматически добавляющего комментарии делает тул, который лишь показывает где их нет.

      Ну знаешь...
      Компилятор тоже не чинит найденные ошибки, а только лишь показывает факт наличия ошибок.


      1. nin-jin
        12.12.2024 16:07

        А неявное приведение типов кто делает, если не компилятор?


      1. aamonster
        12.12.2024 16:07

        Компилятор – нет, а вот IDE предлагает автоматический фикс. Или вот eslint – у него есть флаг --fix, когда он записывает в файл предлагаемые правки (потом смотришь в git, что же он поменял, и коммитишь нужное).

        Да и вашу тулзу не так уж трудно допилить, добавив такой ключик. Всё равно храните стек строк с открывающейся скобкой – никто не мешает оттуда строки брать и записывать в выходной файл.


    1. aabzel Автор
      12.12.2024 16:07

      И никого не смущают файлы на 5к строк

      А сколько по вашему максимум в файле должно быть строк кода?


      1. nin-jin
        12.12.2024 16:07

        Эмпирически, когда их больше 1000 код уже сложно поддерживать и требуется декомпозиция.


    1. DrGluck07
      12.12.2024 16:07

      На уровне HAL используем C, в бизнес-логике C++. И производители микроконтроллеров тоже пишут библиотеки на C. Не на D, прошу заметить, не на Rust, не на Python, не на Go, и ни на чём другом. Вот таки мы странные ребята.


      1. aabzel Автор
        12.12.2024 16:07

        Да просто парень @nin-jin front-ender не туда зашёл по ошибке. Он не со зла.


        1. CitizenOfDreams
          12.12.2024 16:07

          не туда зашёл по ошибке

          Наши сообщения об ошибках ему не понравятся...

          Скрытый текст


          1. aabzel Автор
            12.12.2024 16:07

            Да. Именно так.


          1. randomsimplenumber
            12.12.2024 16:07

            И чтобы таких вот ошибок не было, нужно подписывать закрывающие скобки комментариями и размещать функции в определенном порядке, правильно?


            1. aabzel Автор
              12.12.2024 16:07

              Не нахожу причинно-следственной связи.


              1. nin-jin
                12.12.2024 16:07

                Вы используете язык, переполненный UB, что приводит к непредсказуемому проведению программы, но вместо решения этой проблемы, играетесь с форматированием. И кто из нас фронтендер после этого?


                1. aabzel Автор
                  12.12.2024 16:07

                  Если что, то я вообще против любых комментариев к коду. Просто на работе заставляют их писать в огромном количестве.

                  Как по мне, хороший код понятен и без комментариев.

                  А упор надо делать на модульных тестах


                  1. CitizenOfDreams
                    12.12.2024 16:07

                    Как по мне, хороший код понятен и без комментариев.

                    Комментарии хранят информацию, за которой иначе пришлось бы куда-то лезть:

                    const unsigned char TMR0LoadValue=61; // 40Hz/25ms interrupt rate @1MHz & 1:32 prescaler
                    FVRCON=0b10000001; // FVR and temp sensor; disable ADFVR before sleep for minimum power consumption!
                    PRR=0b00001111; // Power Reduction Register, see PDF page 38

                    Позволяют напомнить себе будущему (или другому несчастному, который полезет в этот код), что именно происходит в данном месте:

                    if ((runMode==2)&&(distanceToGo==0)) // failed to trigger the sensor
                    digitalWrite(ENA,LOW); // just in case, the driver is always on anyway
                    digitalWrite(0,1); // data line high to prevent phantom powering the LEDs

                    Ну или просто так, чтоб было:

                    wdt_reset(); // woof!


                    1. aabzel Автор
                      12.12.2024 16:07

                      const unsigned char TMR0LoadValue=61; // 40Hz/25ms interrupt rate @1MHz & 1:32 prescaler
                      FVRCON=0b10000001; // FVR and temp sensor; disable ADFVR before sleep for minimum power consumption!
                      PRR=0b00001111; // Power Reduction Register, see PDF page 38

                      Боже мой какой хардкод!

                      Не показывайте это никому... Прошу Вас.

                      Крайне рекомендую Вам ознакомится с этим текстом
                      https://habr.com/ru/articles/683762/
                      Архитектура Хорошо Поддерживаемого драйвера для I2C/SPI/MDIO Чипа

                      и Вы забудете, что такое комментарии в Си


                      1. randomsimplenumber
                        12.12.2024 16:07

                        Идут годы, а остроконечники никак не победят тупоконечников ;)


                      1. CitizenOfDreams
                        12.12.2024 16:07

                        Боже мой какой хардкод!

                        Это не универсальный драйвер. Это программа, которая будет работать на одном железе, с одной частотой, с одними настройками таймеров. Предлагаете не хардкодить, а вынести все эти настройки - которые НИКОГДА не будут меняться - в отдельное место? Просто чтоб было по феншую?


                      1. randomsimplenumber
                        12.12.2024 16:07

                        Иногда хочется запустить программу на другом железе. А переписывать не хочется.


                      1. CitizenOfDreams
                        12.12.2024 16:07

                        Иногда хочется запустить программу на другом железе. А переписывать не хочется.

                        А придется. Или переписывать, или изначально писать программы, учитывающие все возможные варианты железа. И вместо "FVRCON=0b10000001" будет три конфигурационных файла, пять библиотек и десять трудноуловимых багов. И все равно в итоге придется что-то исправлять.


                      1. randomsimplenumber
                        12.12.2024 16:07

                        Или переписывать, или изначально писать программы, учитывающие все возможные варианты железа.

                        Или максимально абстрагироваться от железа, а железо-зависимые константы выносить в отдельный файл. Или использовать framework, где привязка к железу уже сделана (Arduino, ага ;)).


                      1. aabzel Автор
                        12.12.2024 16:07

                        Предлагаете не хардкодить, а вынести все эти настройки - которые НИКОГДА не будут меняться - в отдельное место? Просто чтоб было по феншую?


                        Надо придерживаться методологии
                        код отдельно, конфиги отдельно.
                        Это требование ISO26262
                        Вот текст про это
                        ISO 26262-6 разбор документа (или как писать безопасный софт)
                        https://habr.com/ru/articles/757216/


                    1. aabzel Автор
                      12.12.2024 16:07

                      Надо не hardcode(ить) битовые константы как тут (Боже упаси...)

                      const unsigned char TMR0LoadValue=61; // 40Hz/25ms interrupt rate @1MHz & 1:32 prescaler
                      FVRCON=0b10000001; // FVR and temp sensor; disable ADFVR before sleep for minimum power consumption!
                      PRR=0b00001111; // Power Reduction Register, see PDF page 38

                      а создавать константные битовые поля и присваивать им именованные перечисления (слыхали про enum в Си?).

                      Тогда и // /**/ комменты нужны будут как собаке бензобак.
                      Понимаете?

                      Вот так надо

                      const Ltr390Register_t Ltr390Register[]={
                          {
                                  .address=LTR390_REG_ADDR_MEAS_RATE,
                                  .value.MeasRate={ .rate=LTR390_RATE_25_MS, 
                                                    .resolution=LTR390_RESOLUTION_CODE_20_BIT, },
                          },
                      
                          {
                                  .address=LTR390_REG_ADDR_GAIN,   
                      			.value.AlsUvsGain={ .gain=LTR390_GAIN_CODE_18},
                          },
                      };


                      Почитайте про битовые поля в Си. Узнаете что это такое.


                      1. CitizenOfDreams
                        12.12.2024 16:07

                        а создавать константные битовые поля и присваивать им именованные перечисления

                        Нафига? Данная битовая константа берется один раз из даташита и в ходе разработки данной прошивки не меняется. Это все равно что номер квартиры на двери сделать не двумя жестяными цифрами, прикрученными шурупами, а в виде электронного табло, на котором можно задавать числа от -32768 до +32767.


                1. aabzel Автор
                  12.12.2024 16:07

                  Вы используете язык, переполненный UB, что приводит к непредсказуемому проведению программы

                  У компилятора GCC так много ключей для ловли UB что достаточно только собрать бинарь с пучком опций

                   -Werror=address -Werror=switch 
                  -Werror=array-bounds=1 -Werror=comment -Werror=div-by-zero 
                  -Werror=duplicated-cond -Werror=shift-negative-value 
                  -Werror=duplicate-decl-specifier -Werror=enum-compare 
                  -Werror=uninitialized -Werror=empty-body 
                  -Werror=unused-but-set-parameter 
                  -Werror=unused-but-set-variable -Werror=float-equal 
                  -Werror=logical-op -Werror=implicit-int 
                  -Werror=implicit-function-declaration 
                  -Werror=incompatible-pointer-types 
                  -Werror=int-conversion -Werror=old-style-declaration
                  -Werror=maybe-uninitialized -Werror=redundant-decls
                  -Werror=sizeof-pointer-div -Werror=misleading-indentation 
                  -Werror=missing-declarations -Werror=missing-parameter-type
                  -Werror=overflow -Werror=parentheses -Werror=pointer-sign 
                  -Werror=return-type -Werror=shift-count-overflow 
                  -Werror=strict-prototypes -Werror=unused-but-set-variable
                  -Werror=unused-function -Werror=missing-field-initializers
                  -Werror=unused-variable -Werror=unused-but-set-variable 
                  -Werror=implicit-function-declaration 
                  -Werror=unused-variable


                  и всё обычно хорошо. По крайней мере проблем никогда не было.

                  Старость Си это наоборот достоинство так как именно поэтому появились очень зрелые компиляторы. Даже бесплатный ARM-GCC 2021 года.


                  1. nin-jin
                    12.12.2024 16:07

                    int main(void) {
                        int arr[4] = {0, 1, 2, 3};
                        return arr[5];
                    }

                    https://godbolt.org/z/xKTPz4zzh

                    Мне страшно даже представить, сколько в вашем коде ещё не обнаруженных уязвимостей.


                    1. aabzel Автор
                      12.12.2024 16:07

                      Модульные тесты проходят - значит всё хо-ро-шо.


                      1. nin-jin
                        12.12.2024 16:07

                        Значит вы завязались на неопределённое поведение, которое может сломаться в любой момент. Дико осознавать, что фронтендер вынужден объяснять всё это сишнику.

                        Для справки, эквивалентный код на BetterC даже не скомпилируется:

                        extern(C) int main() {
                            int[4] arr = [0, 1, 2, 3];
                            return arr[5];
                        }

                        Error: array index 5 is out of bounds `arr[0 .. 4]`

                        А даже если перенесёте получение индекса в рантайм, то получите падение, а не чтение чего попало из памяти:

                        extern(C) int main() {
                            int[4] arr = [0, 1, 2, 3];
                            int x = 5;
                            return arr[x];
                        }

                        Assertion `array overflow' failed.Program terminated with signal: SIGSEGV


                    1. randomsimplenumber
                      12.12.2024 16:07

                      Хм. Написали код вы, а уязвимости у @aabzel? ;)


    1. aabzel Автор
      12.12.2024 16:07

      и язык разработки из мезозоя

      Почему микроконтроллеры надо программировать именно на Си?

      Какие есть варианты языков для программирования микроконтроллеров? Теоретически подойдет любой компилируемый язык программирования: Assembler, Cи, С++, Rust, Pascal

      Однако выбирают обычно между Си и С++

      1++Cи язык очень легко подается рефакторингу. Переименовывать функции, константы и переменные можно буквально утилитой sed. Прямо из командной строки внутри всего репозитория. При этом код по-прежднему будет собираться и проходить тесты.

      2++У Си есть циклопическое Legacy. Нужный вам код можно взять из ядра Linux или Zephyr.

      3++Программировать на Си проще чем программировать на Assembler или С++

      4++Высокая скорость разработки. В сравнении с Assembler программирование на Си позволяет повысить производительность.

      5++Это компилированный язык. Значит он будет исполняться быстрее чем интерпретированный язык.

      6++Есть указатели.

      7++Есть слово volatile

      8++У компилятора GCC очень много ключей для гибкой настройки различных видов предупреждений на тот или иной синтаксис и семантику.

      9++Вендоры микроконтроллеров дают MCAL именно на Си. Это сотни человеко-лет готовой работы. Поэтому чтобы сэкономить время и использовать их код надо тоже продолжать писать на Си.

      10++Существует бесплатный компилятор GCC

      11++Язык Си простой как ножик. Одни только функции и переменные. Прост в освоении. Тут не будет виртуальных деструкторов, делегатов, шаблонов и прочего. Код выглядит как математические формулы, которые и так все видели в школе.

      12++Достоинство языка Си в том, что он старый 50+ лет и поэтому появились очень зрелые компиляторы. В частности в GCC появилось много опций для выявления UB.

      Чего не хватает?

      1--перегрузки функций. Хотя с этой задачей нормально справляются макросы препроцессора.

      2--знаковых типов данных произвольной разрядности int13_t int7_t. Но этого и так нигде нет.

      Итоги

      Си- это идеальный язык для программирования микроконтроллеров. Компромисс между эффективностью и простотой.


      1. gefestik
        12.12.2024 16:07

        кстати, разработали компилятор Fli-C, который помогает управлять безопасной работой памяти https://www.opennet.ru/opennews/art.shtml?num=62241