Microsoft Visual Studio я считаю лучшей из доступных IDE для разработки C++, которой в основном и занимаюсь. И это при том, что я являюсь идеологическим противником корпорации из Редмонда. Просто IDE для Линукса слабы. Вообще все решения в этой области, основанные на GCC провальны из-за дебаггера. Не знаю, то ли GDB настолько плох, то ли никто не в состоянии нормально встроить его в IDE, то ли он просто идеологически для этого не предназначен. Eclipse c CDT монструозен и тормознут. Vi или emacs с командной строкой и make, которое сразу же посоветует какой-нибудь Линукс-джедай, я лично осилить до того уровня производительности, что имею в MSVS, не в состоянии (а в состоянии ли вообще кто-то?). Впрочем, справедливости ради отмечу, что не сильно-то я и старался.
Альтернативные среды вроде Code::Blocks и Dev-Cpp, когда я последний раз смотрел, были в очень зачаточном состоянии. Вот сегодня скачал первую, собираюсь пересмотреть ещё раз — вдруг что-то изменилось. Но всё это тема для отдельного долгого разговора, перерастающего в холивар. А этот пост именно о Visual C++ и одной её полезной и практически не документированной фиче, на которую я недавно набрёл. Это — кастом визуализаторы (custom visualizers) для переменных — то, что разработчик видит в панельке Watch во время отладки программы. Вещь очень удобная и сильно облегчающая жизнь, если вы, например, как и я, любите писать «велосипеды» в виде собственных библиотек, или вообще часто используете какую-нибудь стабильную библиотеку/фреймворк/движок со своими, сложными для быстрого понимания as is в отладчике, типами данных.
Америку я не открываю, всеобъемлющего мануала тоже не напишу, но, возможно, кому-то нижепреведенная информация окажется полезной. Ещё раз уточню, зачем всё это нужно. Дело в том, что если в отладчике вы посмотрите значение переменной нестандартного типа с достаточной сложной структурой да ещё и, упаси Страуструп, содержащей указатели, то, во-первых, потратите немало времени, просто разбираясь в этой структуре, которая может содержать совершенно не интересные вам детали реализации (например, какие-нибудь умные указатели), во-вторых, регулярно занимаясь пиксель-хантингом по крестикам, раскрывающим бесконечные древообразные списки, и, в-третьих, вообще можете не увидеть там нужные вам данные (к примеру, если какой-то указатель внутри структуры по какой-то причине имеет, скажем, тип void*).
Приведём простой пример. Скажем, мы задались целью написать идеальный класс для текстовой строки, которая умеет хранить как текст в Юникоде, так и однобайтную строку стандартного типа сhar*. При чём пользователь класса не должен заморачиваться насчёт того, что там и как хранится: записал туда однобайтную строку — она и хранится однобайтная, добавил, к примеру, к ней юникодный символ и строка конвертировалась в Юникодную — в таком вот духе. Структура данных для такой строки может выглядеть к примеру так:
class MyString { bool unicode_; // Флаг, true — если хранится строка в Юникоде, false — если однобайтная int length_; // Длина строки union { unsigned short* uData_; // Указатель на Юникодную строку char* bData_; // Указатель на однобайтную строку }; };
Если мы запишем сюда однобайтную строку и честно выставим флажок unicode_ = false, то увидим в отладчике примерно следующее (Microsoft Visual C++ Express 2008):

Если нажать крестик слева, то ситуация будет чуть получше:

Но это же надо нажимать каждый раз, когда надо узнать значение строки, при чём самому «обрабатывать» значение флажка unicode_. Такие вещи в XXI веке, я считаю, должен делать компьютер. Визуализаторы нам в помощь. При разработке на managed языках под .NET, визуализаторы можно писать прямо в коде. Когда-то, когда я программировал на С#, я разбирался с этим, но сейчас уже не помню деталей. В любом случае, эти вещи хорошо документированы в MSDN и особых проблем вызывать не должны. А вот с unmanaged C++ всё по-другому. С незапамятных времён отладчик от MS предоставлял ряд возможностей для настройки через файл autoexp.dat, находящийся по пути:
Начиная с Visual Studio 2005, возможности для настройки серьёзно расширились. autoexp.dat состоит из секций, начало каждой отмечается названием в квадратных скобках, как в ini-файлах. Первая секция [AutoExpand] документирована в комментариях внутри самого файла (комментарии начинаются с «;»). Её возможностей может быть достаточно для решения простых задач, но не для нашего класса строки, так как нам требуется возможность выводить значения разных переменных в зависимости от значения флажка. Иными словами, нам нужен условный оператор. Вторая секция [Visualizer] решает эту проблему. Она не документирована, но autoexp.dat содержит кучу визуализаторов для типов из STL и WinAPI. Разобравшись с тем, что там и как, мы видим, что в решении нашей задачи нет ничего сложного. Итак, по порядку.
- Начнём новый визуализатор. Для этого необходимо указать название нашего типа и открыть фигурные скобки:
MyString {Важно, чтобы фигурная скобка находилась на той же строке, иначе фокус не сработает. Пробелы между названием типа и скобкой допустимы. - Внутри визуализатора может находится несколько блоков — об этом ниже. Пока нас интересует блок preview. Для отделения блока используются круглые скобки. Определим блок.
preview ( )
- Добавим собственно код визуализатора.
preview ( #if(($e.unicode_) == true) ( #([$e.uData_,su], " [", $e.length_, "]") ) #else ( #([$e.bData_,s], " [", $e.length_, "]") ) )На место $e будет подставлено визуализируемое выражение. Блок #( )выполняет слияние строк (задаются в двойных кавычках) и вычисляемых выражений. Если заключить выражение в квадратные скобки, то можно указать дополнительные параметры для визуализации. Например, в нашем случае [$e.uData_,su] явно указывает, что uData_ следует визуализировать, как строку в Юникод. Блок #if — #else интуитивно понятен.
Посмотрим на получившийся результат. Для того, чтобы увидеть изменения, не надо презагружать саму VS, достаточно рестартовать дебаггер.

Теперь работать с нашим типом гораздо удобнее. Детали реализации скрыты от пользователя, и он просто видит то, что и ожидает — значение, хранящееся в строке. А что если пользователя всё-таки заинтересуют эти детали? Например, он всё-таки захочет узнать, является ли данная строка Юникодной. Изучим, какие ещё имеются возможности.
Общий вид визуализатора:
typename[|typename...] {
preview (
; выражение
)
children (
; выражение
)
stringview (
; выражение
)
}
Отметим, что в заголовке визуализатора мы можем через «|» указать несколько типов, для которых будет применён данный визуализатор. Разделы внутри используются следующим образом:
- preview — собственно, для предпросмотра в окне «Watch»;
- children — это блок позволяет нам определить, что будет показано в окошке «Watch», если пользователь «раскроет» значение выражения (нажмёт на крестик слева). Можно использовать специальные возможности #array, #list и #tree, позволяющие визуализировать эти структуры данных.
- stringview — это то, что будет отображаться в «Text Visualizer», окошке, которое можно вызвать кликнув по иконке с изображением увеличительного стекла, справа от значения переменной. Хорошая новость — там можно использовать HTML, плохая новость — в вычисялемых строках нельзя заменять специальные символы HTML на эксейп-последовательности, а также не работают возможности, предлагаемые #array, #list и #tree. Что делает эту возможность мало полезной.
Добавим в children такой код:
children
(
#(
unicode: [$e.unicode_]
)
)
В результате имеем:

И, напоследок, добавим возможность посмотреть, содержимое нашей строки в виде массива символов. Для этого модифицируем код следующим образом:
children
(
#(
unicode: [$e.unicode_],
#if ($e.unicode_)
(
#array (
expr: [$e.uData_[$i],su],
size: $e.length_
)
)
#else
(
#array (
expr: $e.bData_[$i],
size: $e.length_
)
)
)
)
Надеюсь, тут уже всё понятно без подробных пояснений. #array в цикле size раз вычисляет значение в expr и выводит каждое значение, как новый элемент в списке children. В результате, в отладчике видим следующее:

Полный код для нашего визуализатора:
MyString{
preview
(
#if(($e.unicode_) == true)
(
#([$e.uData_,su], " [", $e.length_, "]")
)
#else
(
#([$e.bData_,s], " [", $e.length_, "]")
)
)
children
(
#(
unicode: [$e.unicode_],
#if ($e.unicode_)
(
#array (
expr: [$e.uData_[$i],su],
size: $e.length_
)
)
#else
(
#array (
expr: $e.bData_[$i],
size: $e.length_
)
)
)
)
}
Ссылки
Рекомендую также на заданную тему эти статьи (на английском):
wicharek