Ошибки
21.09.2012
|
AndrewVK |
Задача: есть некий обработчик, для простоты пусть это будет компилятор. В процессе своей работы он может выдавать некоторое количество ошибок. Каждая ошибка характеризуется местом возникновения (файл, строка, колонка), уникальным символьным кодом и текстом с опциональными параметрами на нескольких языках. При этом текст должен выдаваться на языке, который выставлен у потока, выводящего ошибки, а не у потока компиляции (они могут не совпадать).
Вопрос — как сдизайнить прежде всего саму ошибку, чтобы максимально контролировать статически все аспекты при ее формировании.
Язык — C#.
Вопрос — как сдизайнить прежде всего саму ошибку, чтобы максимально контролировать статически все аспекты при ее формировании.
Язык — C#.
... << RSDN@Home 1.2.0 alpha 5 rev. 65 on Windows 8 6.2.9200.0>>
21.09.2012 31 комментарий |
AVK>Вопрос — как сдизайнить прежде всего саму ошибку, чтобы максимально контролировать статически все аспекты при ее формировании.
Ну, насколько я понимаю, в данном контексте "ошибка" — это вполне валидный сценарий. То есть это должно быть не исключение, а просто класс, который в рантайме при создании получает соответствующий текст из соответствующего ресурса...
Или я не догнал сложность проблемы?
IB>Ну, насколько я понимаю, в данном контексте "ошибка" — это вполне валидный сценарий. То есть это должно быть не исключение, а просто класс, который в рантайме при создании получает соответствующий текст из соответствующего ресурса...
IB>Или я не догнал сложность проблемы?
Да пофигу, исключение или класс. Проблема в другом — как сделать типизированным все без кучи рукопашной писанины.
AVK>Да пофигу, исключение или класс. Проблема в другом — как сделать типизированным все без кучи рукопашной писанины.
А где ты видишь "кучу рукопашной писанины"?
AVK>>Да пофигу, исключение или класс. Проблема в другом — как сделать типизированным все без кучи рукопашной писанины.
WH>А где ты видишь "кучу рукопашной писанины"?
Ответ на этот вопрос зависит от конкретного дизайна. Например, при описании каждой ошибки отдельным классом. Если есть возможность как то обойтись без этого — с удовольствием посмотрю. В этом, собственно, вопрос и заключался.
P.S. На всякий случай — я специально выделение добавил в стартовом сообщении.
AVK>P.S. На всякий случай — я специально выделение добавил в стартовом сообщении.
Ну, раз ты настаиваешь на использовании слабого языка тогда можно пошаманить с рефлекшеном.
Перевод можно складывать в XML файлы. Их тоже можно элементарно проверять на вшивость пробежавшись рефлекшеном по свойствам.
PS C# ужасен. Я себе после немерла даже на этих нескольких строках пальцы сломал.
WH>Ну, раз ты настаиваешь на использовании слабого языка тогда можно пошаманить с рефлекшеном.
То есть в ответ на вопрос о том, как сделать чтобы все максимально контролировалось компилятором, ты предлагаешь пошаманить с рефлекшеном?
WH>Перевод можно складывать в XML файлы.
Перевод нужно складывать в файлы resx.
WH>PS C# ужасен. Я себе после немерла даже на этих нескольких строках пальцы сломал.
Вопрос привычки.
AVK>То есть в ответ на вопрос о том, как сделать чтобы все максимально контролировалось компилятором, ты предлагаешь пошаманить с рефлекшеном?
Ты код то смотрел?
Errors.Error1.New(file, line)
Куда уж больше контроля компилятором?
Можно конечно еще круче, но не на C#.
Если бы не требование "чисто C#" то все бы решалось простейшим макросом, который можно написать, не приходя в сознание.
А без генератора кода ничего умнее чем то, что я показал, ты не придумаешь.
WH>>Перевод можно складывать в XML файлы.
AVK>Перевод нужно складывать в файлы resx.
А давай ты сразу все требования озвучишь.
WH>>PS C# ужасен. Я себе после немерла даже на этих нескольких строках пальцы сломал.
AVK>Вопрос привычки.
Вопрос писанины. Её намного больше чем нужно.
Например, в немерле не нужно писать такие тупые конструкторы.
AVK>Перевод нужно складывать в файлы resx.
С учетом совокупности требований:
1) язык C#
2) ресурсы в resx
3) максимальный статический контроль
Я сделал бы собственный процессор resx файлов, создающий обвязку в духе продемонстрированной WolfHound-ом. Вот этот класс:
Сгенерировался бы по resx файлу означенной тулзой. В случае, если наше сообщение параметризовано (например текст в resx имеет формат подстановки string.Format), я бы генерировал статические методы с соответствующими параметрами.
Собственно кодогенерацию я бы выполнил с помощью NRefactory.
PS Да в Nemerle тожесамое сделал бы несложный макрос за значительно меньшее число знаков.
WH>>PS C# ужасен. Я себе после немерла даже на этих нескольких строках пальцы сломал.
AVK>Вопрос привычки.
В точку.
AVK>Задача: есть некий обработчик, для простоты пусть это будет компилятор. В процессе своей работы он может выдавать некоторое количество ошибок. Каждая ошибка характеризуется местом возникновения (файл, строка, колонка), уникальным символьным кодом и текстом с опциональными параметрами на нескольких языках. При этом текст должен выдаваться на языке, который выставлен у потока, выводящего ошибки, а не у потока компиляции (они могут не совпадать).
AVK>Вопрос — как сдизайнить прежде всего саму ошибку, чтобы максимально контролировать статически все аспекты при ее формировании.
AVK>Язык — C#.
На яве я бы сделал так:
А имплементацию бы создавал либо динамическим proxy либо кодогенерацией во время сборки.
По полному имени метода можно извлекать соответствующий ресурс из bundle в зависимости от языка.
В юзерский код имплементация вставляется DI с помощью IoC Container.
Удобно тестировать — элементарно писать моки и expectation вызова конкретного метода, чем парсить текстовый вывод ошибок.
.> void error1(Position pos, String symbol, String param1);
.> void error2(Position pos, int number);
.> void warn42(Position pos);
А читать вызывающий код как потом? warn42 как то не очень дескриптивен.
.>Удобно тестировать — элементарно писать моки и expectation вызова конкретного метода, чем парсить текстовый вывод ошибок.
Чтобы не парсить текст ошибки как раз символьный errorcode и нужен
.>> void error1(Position pos, String symbol, String param1);
.>> void error2(Position pos, int number);
.>> void warn42(Position pos);
AVK>А читать вызывающий код как потом? warn42 как то не очень дескриптивен.
Если тупой javadoc (или чего там такое в c#?) не устроит, но можно сделать соглашение именования, типа warn42_shortDescription.
.>>Удобно тестировать — элементарно писать моки и expectation вызова конкретного метода, чем парсить текстовый вывод ошибок.
AVK>Чтобы не парсить текст ошибки как раз символьный errorcode и нужен
Мне кажется, что для тестов часто нужен не только код, но и параметры. Например, "E1234: symobol 'aaa' not found" нужно же убедиться, что именно aaa не найден.
.>Мне кажется, что для тестов часто нужен не только код, но и параметры. Например, "E1234: symobol 'aaa' not found" нужно же убедиться, что именно aaa не найден.
Но это уже будут не юнит-тесты. Вот пример тестов таких сообщений.
.>>Мне кажется, что для тестов часто нужен не только код, но и параметры. Например, "E1234: symobol 'aaa' not found" нужно же убедиться, что именно aaa не найден.
H>Но это уже будут не юнит-тесты. Вот пример тестов таких сообщений.
Не понял почему.
Примерно так на яве будет:
И никаких магических регекспов.
.>И никаких магических регекспов.
Все не так просто, "магические регекспы" помимо шаблона сообщения несут в себе еще и номер строки. Приведенный пример — это негативный тест, т.е. файл, компиляция которого завершается с ошибкой. Есть еще и позитивные тесты — они компилируются без ошибки (но возможно с предупреждениями, которые нужно аннотировать в духе // W: ....., а также строки на которых заведомо не должно быть сообщений // OK) и в самом файле есть разметка содержащая выхлоп тестовой программы.
.>>И никаких магических регекспов.
H>Все не так просто, "магические регекспы" помимо шаблона сообщения несут в себе еще и номер строки.
Номер строки можно тоже проверять, у меня там проверяется.
H>Приведенный пример — это негативный тест, т.е. файл, компиляция которого завершается с ошибкой. Есть еще и позитивные тесты — они компилируются без ошибки (но возможно с предупреждениями, которые нужно аннотировать в духе // W: ....., а также строки на которых заведомо не должно быть сообщений // OK) и в самом файле есть разметка содержащая выхлоп тестовой программы.
А, понял. В смысле сам тестовый файл используется как источник того, что надо проверить? Выцепляются комменты вида "// [WE]:" из текста специальной тулзой?
Да, тоже интересный подход, но наверное слишком специфичный для компиляторов.
.>Мне кажется, что для тестов часто нужен не только код, но и параметры.
Для тестов ничего парсить не нужно, достаточно точного соответствия полного сообщения.
AVK>Задача: есть некий обработчик, для простоты пусть это будет компилятор. В процессе своей работы он может выдавать некоторое количество ошибок. Каждая ошибка характеризуется местом возникновения (файл, строка, колонка), уникальным символьным кодом и текстом с опциональными параметрами на нескольких языках. При этом текст должен выдаваться на языке, который выставлен у потока, выводящего ошибки, а не у потока компиляции (они могут не совпадать).
AVK>Вопрос — как сдизайнить прежде всего саму ошибку, чтобы максимально контролировать статически все аспекты при ее формировании.
AVK>Язык — C#.
В теории будет достаточно отдельного resX-файла с описанием текста ошибок + замены генератора на кастомный на t4 + базового типа, чтобы генерить поменьше кода. Разумеется, придётся придерживаться определённой схемы именования ресурсов (..._Message, ..._Param0, ..._Param1 etc), чтобы генератор понимал что есть что.
На практике с прямым использованием .tt-файлов вечно возникают грабли, особенно если распространять tt-шаблоны как часть проекта. Навскидку, в 2008й приходилось править ключи реестра для использования относительных путей к шаблонам. На мой взгляд, проще использовать свой custom tool, и из него вызывать t4-процессор.
Мне попадались примеры с заменой генератора resx (насколько оно рабочее на практике — хз)
http://www.codeproject.com/Articles/31907/Customize-the-Code-Generated-by-the-Resources-Desi
http://www.codeproject.com/Articles/269362/Using-T4-Templates-to-generate-custom-strongly-typ
http://blog.baltrinic.com/software-development/dotnet/t4-template-replace-resxfilecodegenerator
+ можно использовать T4 Toolbox
http://www.olegsych.com/2009/11/t4-toolbox-automatic-template-transformation/
Если не подходит — всегда можно будет извратиться с IVsSingleFileGenerator
AVK>Задача: есть некий обработчик, для простоты пусть это будет компилятор. В процессе своей работы он может выдавать некоторое количество ошибок. Каждая ошибка характеризуется местом возникновения (файл, строка, колонка), уникальным символьным кодом и текстом с опциональными параметрами на нескольких языках. При этом текст должен выдаваться на языке, который выставлен у потока, выводящего ошибки, а не у потока компиляции (они могут не совпадать).
AVK>Вопрос — как сдизайнить прежде всего саму ошибку, чтобы максимально контролировать статически все аспекты при ее формировании.
AVK>Язык — C#.
Если я правильно понял задачу, то:
Если принципиальное абстрактное решение, то:
Есть "фреймворк ошибок": базовые классы ошибок, хэлперы всякие.
Есть хранилище ресурсов — я так понял вы непременно хотите resx.
Есть хранилище дополнительной информации об ошибках: соответствие кодов и сообщений/северити/кол-ва доп параметров
Есть генерилка, которая на основе фреймворка и 2ух хранилищ делает результирующие ошибки.
Как я понимаю, это фактически просто переформулировка требований и тут врядли какие-то вопросы.
А дальше надо взвешивать в зависимости от того, какую роль будет играть этот модуль в проекте, сколько людей будет его использовать, насколько технически грамотные и т.д., и т.д. Решений много может быть.
Конкретно с такой задачей я не сталкивался, но в похожих я склонен делать хранилище доп. информации XML-кой, создавать самописный редактор такой XML-ки, и делать самописный же генератор на основе XSLT.
Что именно вам нужно во "фреймворке" не очень понятно, пока приходит на ум только простенький ErrorBase. Возможно понадобятся хэлперы для вычисления языка, строчки и т.д.