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

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="строка выключена";

А всего существует два десятка возможных вариантов: D0 - D9 и R0 - R9. И вот так-то они работают:

ОК, едем дальше:

> debrel
DebRel v1.0 (2025-06-03), Debug/Release comment converter, freeware
Copyright (c) Sergey P. Derevyago

Usage:
debrel [-c#] A     file  D0-5-10 [R5-20-30 D9-50-100 ...]
debrel [-c#] R0|D5 file1 [file2 file3 ...]
debrel [-c#] C|U   file1 [file2 file3 ...]

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

Все вполне очевидно и просто. В этом, кстати, и была цель.

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

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

Кроме нескольких версий 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. И тут самое время подумать, что размер указателя нам известен на стадии сборки...

Сразу просятся R4/R8:

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))) //R8|
//R4|    lo := runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(seed), uintptr(len))
//R4|    hi := runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(seed>>32), uintptr(len))
//R4|    return uint64(hi)<<32 | uint64(lo)
}

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


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

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

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

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

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