Сергей Деревяго

Debug/Release конвертер комментариев



1. Введение

Ну зачем еще это читать и причем тут Архитектура?

Я тоже был когда-то не уверен: конвертер комментариев влияет на Основы разработки?? Гмм... А ведь влияет!

И речь пойдет об Удивительной Находке для всех промышленных языков программирования. Вы не ослышались: для всех!

С уважением, Сергей Деревяго.


2. Суть проблемы

Каким мы видим идеальный метод управления Debug/Release конфигурациями?
  1. Debug конфигурация дает нам всю необходимую диагностику. Еще желательно с различной глубиной.
  2. В Release конфигурации нет никаких следов Debug-а. Ни байта!
Не бывает?

А ведь так оно есть! В мире C/C++, посредством допотопного assert()-а.

Почему ж его нет у других?? Вероятно, на то есть причины:

Современные языки четко знают свою аудиторию... Препроцессор велено не пущать.

Но как вы уже поняли, можно и без него. Тот кто нам мешает, тот нам поможет.

А начнем мы с примера реального кода:

#ifndef NDEBUG
struct Debug {
    mutex mtx;
    int_map<uint32_t, uint32_t> allocated;

    Debug() {}
    Debug(const int_map<uint32_t, uint32_t>* a) : allocated(a) {}
};
#endif

void off_pool::free(offset off, uint32_t sz)
{
    using namespace off_pool_cpp;

#ifndef NDEBUG
    {
        auto dbg=(Debug*)debug;
        lock_guard<mutex> lock(dbg->mtx);

        int pos=dbg->allocated.find(off);
        assert(pos!=-1);
        assert(*dbg->allocated.pval(pos)==sz);
        dbg->allocated.erase(off);
    }
#endif

    uint32_t asz=toAsz(sz);
    // ...
}

Это классика жанра и здесь все очень просто: в Release конфигурации строки кода между #ifndef NDEBUG и #endif полностью исчезают, и мы получаем идеальный билд. Ни единого лишнего байта!

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

//D5|struct Debug {
//D5|    mutex mtx;
//D5|    int_map<uint32_t, uint32_t> allocated;
//D5|
//D5|    Debug() {}
//D5|    Debug(const int_map<uint32_t, uint32_t>* a) : allocated(a) {}
//D5|};

void off_pool::free(offset off, uint32_t sz)
{
    using namespace off_pool_cpp;

//D5|    {
//D5|        auto dbg=(Debug*)debug;
//D5|        lock_guard<mutex> lock(dbg->mtx);
//D5|
//D5|        int pos=dbg->allocated.find(off);
//D5|        perm_assert(pos!=-1);
//D5|        perm_assert(*dbg->allocated.pval(pos)==sz);
//D5|        dbg->allocated.erase(off);
//D5|    }

    uint32_t asz=toAsz(sz);
    // ...
}

Как бы можно, и что же?

Ну а то, что в случае Debug конфигурации, все эти едкие комментарии потребно выставить на мороз! И вот тут уже нужен DebRel -- один запуск программы и опа:

struct Debug {                                                    //D5|
    mutex mtx;                                                    //D5|
    int_map<uint32_t, uint32_t> allocated;                        //D5|
                                                                  //D5|
    Debug() {}                                                    //D5|
    Debug(const int_map<uint32_t, uint32_t>* a) : allocated(a) {} //D5|
};                                                                //D5|

void off_pool::free(offset off, uint32_t sz)
{
    using namespace off_pool_cpp;

    {                                               //D5|
        auto dbg=(Debug*)debug;                     //D5|
        lock_guard<mutex> lock(dbg->mtx);           //D5|
                                                    //D5|
        int pos=dbg->allocated.find(off);           //D5|
        perm_assert(pos!=-1);                       //D5|
        perm_assert(*dbg->allocated.pval(pos)==sz); //D5|
        dbg->allocated.erase(off);                  //D5|
    }                                               //D5|

    uint32_t asz=toAsz(sz);
    // ...
}

Вся доступная диагностика у нас снова в бою!


3. Утилита debrel

Итак, что же делает утилита debrel? Она просто переставляет узнаваемые комментарии из начала в конец строки, тем самым включая и выключая помеченные вами блоки:

       s1="строка включена"; //D5|
//D6|  s2="строка выключена";

А всего существует два десятка Debug/Release вариантов и хреналеон дополнительных -- D0-D9/R0-R9 плюс особые Sxxx:

Все! Пора запускать:

> debrel
DebRel v1.2 (2025-12-10), Debug/Release comment converter, freeware
Copyright (c) Sergey P. Derevyago

Usage:
debrel [-c#]   -A    -D0-5-10 [-~R5-20-30 ...] file
debrel [-c#] -D5|-R0 [-Spec1 ...] file1 [file2 ...]
debrel [-c#]  -C|-U  file1 [file2 ...]
debrel   /* ... */   -r    path   mask
debrel   /* ... */   -f    files.txt

Пройдемся по пунктам:

Все логично и просто.

4. Полезные практики

Итак, хозяйке на заметку:
  1. Шуруп, забитый молотком, держится крепче, чем гвоздь, закрученный отверткой.
  2. Не пытайтесь разруливать сложные случаи! DebRel для типичных проблем.
  3. Используйте D5 по умолчанию. А D3 и D7 для поменьше/побольше отладки.
  4. Используйте R0 по умолчанию. Но Release комментариев обычно не будет вообще.
  5. Храните исходники в основной Release конфигурации. Отладка для разработчиков.
Не, ну вроде бы ясно. Но зачем специальные цели?? Ни Debug, ни Release -- поперек!

А возьмем, например, эту страшную функцию:

func rthash(buf []byte, seed uint64) uint64 {
    if len(buf) == 0 {
        return seed
    }
    len := len(buf)
    if goarch.PtrSize == 8 {
        return uint64(runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(seed), uintptr(len)))
    }
    lo := runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(seed), uintptr(len))
    hi := runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(seed>>32), uintptr(len))
    return uint64(hi)<<32 | uint64(lo)
}

Она компилируется на 64- и 32-битную архитектуру одновременно и поэтому проверяет goarch.PtrSize. И тут самое время подумать, что размер указателя нам известен на стадии сборки...

Сразу просятся специальные:

func rthash(buf []byte, seed uint64) uint64 {
    if len(buf) == 0 {
        return seed
    }
    len := len(buf)
    return uint64(runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(seed), uintptr(len))) //S_64|
//S_32|    lo := runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(seed), uintptr(len))
//S_32|    hi := runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(seed>>32), uintptr(len))
//S_32|    return uint64(hi)<<32 | uint64(lo)
}

Хоть маленечко смысла в гуглопомойке!


5. Заключение

Нам не дано предугадать, как слово наше отзовется... Как ни смешно будет звучать, но пострадали стоматологи!

Появился DebRel -- стало меньше зубовного скрежета. То, что в C/C++ занимает секунды, теперь стало доступно для всех:

Но довольно теории, пробуйте! DebRel мгновенно упрощает Жизнь.

6. История изменений

6.1. DebRel v1.1

По просьбам трудящихся, были добавлены инвертированные цели вида //~D5: Тем самым, у нас появилась возможность задать вариант "не D5". Например:

1
        pn, ok := my.wr.Get(kpn)
//D5|   lib.Assert(ok)
2
        pn, ok := my.wr.Get(kpn)
//D5|   lib.Assert(ok)
        _ = ok //~D5|

Здесь мы с помощью //~D5 предотвратили ошибку "declared and not used: ok", т.к. ее использует только Assert(). Еще пример:

1
//R0|   bm := maps.NewBytesMap(0)
//R1|   bm := maps.NewBytesMap(N)
2
        bm := maps.NewBytesMap(0) //~R1|
//R1|   bm := maps.NewBytesMap(N)

Как можно видеть, конфигурация D5 выключает оба определения bm, и мы получаем "undefined: bm". Но //~R1| спасает ситуацию...

Жить стало лучше, жить стало веселее!


6.2. DebRel v1.1.1

That’s one small step for a man, but one giant leap for mankind!

Да-да, я тоже знаю, что не было их на Луне. Только эта подверсия и правда гигантский скачок:

DebRel v1.1.1 (2025-10-20), Debug/Release comment converter, freeware
Copyright (c) Sergey P. Derevyago

Usage:
debrel [-c#] A     file  D0-5-10 [R5-20-30 D9-50-100 ~D9-101-105 ...]
debrel [-c#] R0|D5 file1 [file2 file3 ...]
debrel [-c#] C|U   file1 [file2 file3 ...]
debrel /* ... */   -r    path   mask
debrel /* ... */   -f    files.txt

Мы существенно скрасили Жизнь всем категориям граждан:

Первый случай прекрасен для мелких проектов: один вызов debrel D5 -r . *.go и все получили по... Да, есть варианты!

Ну а самый надежный и точный -- второй. Щепотка усилий, и вы полностью контролируете ситуацию.

Но бывает, что хочется Чуда...

Да, такое мы можем. Вам теперь и под Windows дозволено задавать маски в именах директорий: examples\*\*.go вместо file1 [file2 file3]. Ну разве не песня?


6.3. DebRel v1.2

Все же надо себя и побаловать! В это сложное, как всегда, Время.

И какая сейчас Суперсила? Зело цели спецназначения!

Это цели с большой буквы S и длиною не более 32 символов. Допускаются буквы и цифры, также символ подчеркивания. Все как положено:

DebRel v1.2 (2025-12-10), Debug/Release comment converter, freeware
Copyright (c) Sergey P. Derevyago

Usage:
debrel [-c#]   -A    -D0-5-10 [-~R5-20-30 ...] file
debrel [-c#] -D5|-R0 [-Spec1 ...] file1 [file2 ...]
debrel [-c#]  -C|-U  file1 [file2 ...]
debrel   /* ... */   -r    path   mask
debrel   /* ... */   -f    files.txt

Может сразу не видно, но они дополнительные! Без Debug и Release не укажешь.


Copyright © С. Деревяго, 2025

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