Вот такие вопросы еще задают на знание паттернов проектирования.
Описать недостатки паттерна singleton. Предложить решения устранения недостатков.
— Откуда вы знаете, что я ненормальная? — спросила Алиса.
— Потому что ты тут, — просто сказал Кот. — Иначе бы ты сюда не попала.
— Потому что ты тут, — просто сказал Кот. — Иначе бы ты сюда не попала.
5 декабря 2008 г.
20 октября 2008 г.
GCC и precision loss
В процессе портирования нашего линуксового проекта на Windows обнаружилась интересная вещь: наш компилятор GCC не умеет распознавать ситуации с присвоением из
Вот простой пример:
Я, честно говоря, был немало удивлен. Специально этим вопросом не занимался, но был уверен, что такие ситуации GCC отслеживать умеет. Ан нет... Что самое интересное, большинство моих знакомых гуру тоже были в этом уверены. В нашем проекте, как и во многих других, известных мне проектах, используется GCC 4.1.2, некоторые используют 4.2. Вроде бы есть опция
Думаю, что о важности такого предупреждения говорить не нужно. В MSVC++ эта ситуация легко отслеживается включением третьего уровня предупреждений —
Но самое интересное не в этом. После длительного изучения документации по GCC, все-таки нашел :)
Итак, не прошло и 25 лет, как разработчики GCC все таки прониклись пользой отслеживания размерности типов. Начиная с версии 4.3 (первый релиз вышел в марте 2008 года, то есть, всего полгода назад) GCC умеет распознавать и сообщать о ситуациях с присваиманием из long в char и им подобных.
Опция
Я проверил — на приведенный пример ругается "warning: conversion to ‘short unsigned int’ from ‘long unsigned int’ may alter its value". Аллелуйа!
Ура, товарищи! Даешь новые версии! Даешь новые баги! :)
long в short и им подобные. То есть, распознавать, скорее всего, умеет, а вот сообщать о них — нет. Вот простой пример:
int main (int argc, char **argv)
{
unsigned long a = 0xFFFFFFFE;
unsigned char b = a + 1;
return 0;
}
Я, честно говоря, был немало удивлен. Специально этим вопросом не занимался, но был уверен, что такие ситуации GCC отслеживать умеет. Ан нет... Что самое интересное, большинство моих знакомых гуру тоже были в этом уверены. В нашем проекте, как и во многих других, известных мне проектах, используется GCC 4.1.2, некоторые используют 4.2. Вроде бы есть опция
-Wconversion, но работает она только для fixed и floating point. В общем, подстава.Думаю, что о важности такого предупреждения говорить не нужно. В MSVC++ эта ситуация легко отслеживается включением третьего уровня предупреждений —
/W3. После портирования нашего проекта на MSVC++, мы смогли легко локализовать и исправить несколько ошибок, связанных как раз с некорректным преобразованием типов.Но самое интересное не в этом. После длительного изучения документации по GCC, все-таки нашел :)
Итак, не прошло и 25 лет, как разработчики GCC все таки прониклись пользой отслеживания размерности типов. Начиная с версии 4.3 (первый релиз вышел в марте 2008 года, то есть, всего полгода назад) GCC умеет распознавать и сообщать о ситуациях с присваиманием из long в char и им подобных.
Опция
-Wconversion, которая работала только для fixed и floating point теперь разделена на две опции -Wtraditional-conversion и -Wconversion. Подробности можно почитать здесь http://gcc.gnu.org/wiki/NewWconversionЯ проверил — на приведенный пример ругается "warning: conversion to ‘short unsigned int’ from ‘long unsigned int’ may alter its value". Аллелуйа!
Ура, товарищи! Даешь новые версии! Даешь новые баги! :)
8 октября 2008 г.
Задачи на собеседованиях: наследование в C++
Очередной вопрос с собеседования, который часто спрашивают.
У нас есть некая иерархия классов. Нам нужно запретить наследование от какого-то класса. В Java для этого есть специальное ключевое слово final, но в C++ такого слова нет. Попытка наследования от финального класса должна пресекаться на этапе компиляции.
У нас есть некая иерархия классов. Нам нужно запретить наследование от какого-то класса. В Java для этого есть специальное ключевое слово final, но в C++ такого слова нет. Попытка наследования от финального класса должна пресекаться на этапе компиляции.
Теги:
задачи,
программирование,
собеседования,
cpp
15 сентября 2008 г.
Javascript: "option" object и IE
Простой скриптик. Берет все input-элементы с именем "hiddenlist" и заносит их в выпадающий список "selectlist". Отображаться в списке должно значение
Почему-то в IE после вызова
Пришлось сделать так:
В этом варианте работает в обоих браузерах. Мне, в общем-то, все равно, хотя второй вариант мне кажется менее приятным для глаз. Однако, непонятно почему первый вариант не работает в IE.
value, а на сервер отсылаться значение id. В Firefox работает, а в Internet Explorer 7 — нет.
var elems = document.getElementsByName("hiddenlist");
var selectobj = document.getElementById("selectlist");
removeAllChildren(selectobj);
for (i = 0; i < elems.length; ++i)
{
var optobj = document.createElement("option");
optobj.value = elems[i].id;
optobj.text = elems[i].value;
selectobj.appendChild(optobj);
}
Почему-то в IE после вызова
appendObject(optobj) поле text очищается, или просто игнорируется. А поле optobj.value обрабатывается нормально. В результате в IE после вызова скрипта имею список из N пустых строк, но при этом на сервер отсылаются правильные значения.Пришлось сделать так:
for (i = 0; i < elems.length; ++i)
{
selectobj.appendChild(document.createElement("option"));
selectobj.options[i].value = elems[i].id;
selectobj.options[i].text = elems[i].value;
}
В этом варианте работает в обоих браузерах. Мне, в общем-то, все равно, хотя второй вариант мне кажется менее приятным для глаз. Однако, непонятно почему первый вариант не работает в IE.
Теги:
грабли,
программирование,
Firefox,
IE,
javascript
12 сентября 2008 г.
Exceptions и Visual C++
Недавно понадобилось собрать STL-проект на Visual C++ 2008 без использования исключений.
Почитал мануал по компилятору, долго игрался с настройками проекта и ключами
Наконец, залез в код STL и обнаружил там некий волшебный макрос
Припоминаю, что лет шесть назад пытался решить эту же задачу для Visual C++ 6.0. И решалась она абсолютно таким же способом, через
С праздником вас, дорогие коллеги! С днем программиста! :)
Updated: Если нужно линковаться с msvcrt.lib или msvcrtd.lib (опции компилятора /MD или /MDd), то нужно определить еще один макрос:
Updated: Вот интересная статья в тему http://ccollomb.free.fr/blog/?p=34
Почитал мануал по компилятору, долго игрался с настройками проекта и ключами
/EHsc... Что-то никак не получается отключить исключения.Наконец, залез в код STL и обнаружил там некий волшебный макрос
_HAS_EXCEPTIONS. Добавление в опции компиляции ключа -D_HAS_EXCEPTIONS=0 привело к нужному результату — исключения в STL отключились.Припоминаю, что лет шесть назад пытался решить эту же задачу для Visual C++ 6.0. И решалась она абсолютно таким же способом, через
_HAS_EXCEPTIONS. Вот я и думаю, то ли Microsoft что-то перемудрил с Visual C++, то ли я его "готовить" не умею?..С праздником вас, дорогие коллеги! С днем программиста! :)
Updated: Если нужно линковаться с msvcrt.lib или msvcrtd.lib (опции компилятора /MD или /MDd), то нужно определить еще один макрос:
#define _STATIC_CPPLIBUpdated: Вот интересная статья в тему http://ccollomb.free.fr/blog/?p=34
Теги:
грабли,
программирование,
exceptions,
STL,
VC++
2 сентября 2008 г.
Задачи на собеседованиях: графы
Задача про графы, которую часто задают на собеседованиях. Для людей, которые ранее не сталкивались с графами, неплохая разминка для мозгов.
У нас есть ориентированный граф и его матрица смежности. Необходимо разработать алгоритм, вычисляющий количество узлов графа, участвующих в циклах. Речь идет просто о количестве узлов. Нас не интересуют ни количество циклов, ни количество узлов в каждом цикле. Только общее количество узлов, задействованных в циклах данного графа.
UDP: Кстати, найдя кол-во узлов мы сможем ответить на общий вопрос о наличии цикла в графе. Иногда вопрос звучит именно так.
Теги:
графы,
задачи,
программирование,
собеседования
3 июля 2008 г.
Как сменить shell
Тема не новая, но вот сегодня опять занимался решением старых проблем...
Не знаю кому как, но лично я считаю, что лучший shell — это bash. Не буду перечислять все преимущества, но одно просто неоспоримое — привык я к нему :) Я вообще люблю консоль, но bash — особенно. Он какой-то уютный, что ли.
Но вот сегодня захожу на очередной удаленный сервер по ssh и попадаю в ненавистный мне tcsh. Всё б ничего и можно потерпеть, но хочется уюта :) Но я здесь не root, и мне особо не рады. Что делать?
Исправляется довольно просто: нужно прописать смену shell в настроечном файле, который считывается при логине. Для tcsh этот файл называется
Эти строки заменяют существующий shell, загружаемый по умолчанию, на bash. В данном случае по умолчанию был установлен tcsh.
В случае другого shell решение немного изменится. Например, если по умолчанию загружается sh, то никакой .login не поможет — sh просто не знает о существовании такого файла. Для sh нужно исправлять файл
Маленький нюанс здесь — bash тоже считывает .profile и двойной if нужен здесь для того, чтобы исключить вероятность бесконечной рекурсии при загрузке bash.
Полный список соответствия разных shell'ов и настроечных файлов можно почитать здесь.
Не знаю кому как, но лично я считаю, что лучший shell — это bash. Не буду перечислять все преимущества, но одно просто неоспоримое — привык я к нему :) Я вообще люблю консоль, но bash — особенно. Он какой-то уютный, что ли.
Но вот сегодня захожу на очередной удаленный сервер по ssh и попадаю в ненавистный мне tcsh. Всё б ничего и можно потерпеть, но хочется уюта :) Но я здесь не root, и мне особо не рады. Что делать?
Исправляется довольно просто: нужно прописать смену shell в настроечном файле, который считывается при логине. Для tcsh этот файл называется
.login и находиться должен в моей домашней папке. Если его нет, то нужно создать и прописать в нем следующее:
if (-f /bin/bash) then
setenv SHELL /bin/bash
exec /bin/bash --login
end if
Эти строки заменяют существующий shell, загружаемый по умолчанию, на bash. В данном случае по умолчанию был установлен tcsh.
В случае другого shell решение немного изменится. Например, если по умолчанию загружается sh, то никакой .login не поможет — sh просто не знает о существовании такого файла. Для sh нужно исправлять файл
.profile (он тоже должен находиться в домашней папке):
if [ $SHELL = "/bin/sh" ]; then
if [ -f /usr/bin/bash ]; then
SHELL=/usr/bin/bash
export SHELL
exec /usr/bin/bash --login
fi
fi
Маленький нюанс здесь — bash тоже считывает .profile и двойной if нужен здесь для того, чтобы исключить вероятность бесконечной рекурсии при загрузке bash.
Полный список соответствия разных shell'ов и настроечных файлов можно почитать здесь.
16 июня 2008 г.
Блокирующие вызовы и ошибка EINTR
Недавно столкнулся с ошибкой, связанной с неправильной обработкой возвращаемого значения функции
Функция
Вот пример неправильной обработки возвращаемого значения:
Что здесь неправильно? Дело в том, что функция
При этом никакой ошибки на самом деле нет, это просто способ дать программе обработать пришедший сигнал. Это необходимо учитывать, и вызов
Это касается и других блокирующих функций. Например,
sem_wait().Функция
sem_wait() уменьшает текущее значение семафора на 1. Если прежнее значение семафора было 0, то поток, выполняющий функцию sem_wait, блокируется до изменения значения семафора. При успешном выполнении — функция возвращает 0; при ошибке — вернет -1, а errno будет содержать код ошибки.Вот пример неправильной обработки возвращаемого значения:
int res = sem_wait(&sem);
if (0 != res)
{
// Ошибка!
// Сообщаем об ошибке и выходим.
...
return;
}
// Ошибки нет - продолжаем работу.
...
Что здесь неправильно? Дело в том, что функция
sem_wait() является блокирующим системным вызовом. Поэтому выполнение этой функции может быть прервано, если выполняющийся поток получит какой-нибудь сигнал (например, сигнал о завершении другого потока). В этом случае функция вернет -1, а errno будет равна EINTR.При этом никакой ошибки на самом деле нет, это просто способ дать программе обработать пришедший сигнал. Это необходимо учитывать, и вызов
sem_wait() лучше всего делать в цикле. Например, таком:
int res;
do
{
res = sem_wait(&sem);
}
while (0 != res && EINTR == errno);
if (0 != res)
{
// Ошибка!
// Сообщаем об ошибке и выходим.
...
return;
}
// Ошибки нет - продолжаем работу.
...
Это касается и других блокирующих функций. Например,
select, wait, waitpid и др. Возможность прерывания функции с ошибкой EINTR обычно описана в man'е.
20 мая 2008 г.
Ошибки кодирования
Очередной багфиксинг показал, что за 30 лет мало что изменилось :)
Недавно разбирались в причинах нестабильной работы модуля логгирования. Модуль, в общем-то, довольно простой. На вход подается строка, содержащая текст сообщения, а модуль занимается тем, что записывает это сообщение в журнал. Каждый из компонентов системы должен пользоваться этим модулем, для журналирования своих сообщений.
И вот, модуль логгирования вдруг стал время от времени "падать" в совершенно непредсказуемые моменты. Оказалось, что виноват в этом один из компонентов, который вместо осмысленного текста посылает в модуль логгирования мусор. У компонента проблема, но почему модуль-то падает?..
Ошибка стара, как Си :) Разработчик модуля логгирования реализовал функцию примерно так:
Видно ошибку? Проблема тут в том, что текст сообщения напрямую подается в функцию
В нашем случае на вход функции подавался случайный мусор, поэтому время от времени в нем встречалась последовательность
Вот правильное использование
Вызов
Читать об уязвимостях printf.
Технологии мчатся вперед, а ошибки не меняются :)
Недавно разбирались в причинах нестабильной работы модуля логгирования. Модуль, в общем-то, довольно простой. На вход подается строка, содержащая текст сообщения, а модуль занимается тем, что записывает это сообщение в журнал. Каждый из компонентов системы должен пользоваться этим модулем, для журналирования своих сообщений.
И вот, модуль логгирования вдруг стал время от времени "падать" в совершенно непредсказуемые моменты. Оказалось, что виноват в этом один из компонентов, который вместо осмысленного текста посылает в модуль логгирования мусор. У компонента проблема, но почему модуль-то падает?..
Ошибка стара, как Си :) Разработчик модуля логгирования реализовал функцию примерно так:
void logMessage (const char *str)
{
printf(str);
}
Видно ошибку? Проблема тут в том, что текст сообщения напрямую подается в функцию
printf, минуя использование форматных строк типа "%s\n". Если подавать на вход функции осмысленный текст, то все будет хорошо, до тех пор пока в тексте сообщения не встретится "%s". В этом случае функция printf попытается сформировать новую строку, предполагая, что в стеке находятся дополнителльные аргументы. А аргументов-то там и нет. Вот тут-то printf и упадет с большой долей вероятности (скорее всего это произойдет, когда printf попытается вызвать strlen для значения, которое достанет из стека).В нашем случае на вход функции подавался случайный мусор, поэтому время от времени в нем встречалась последовательность
"%s", и модуль вываливался с ошибкой Segmentation fault.Вот правильное использование
printf для нашей функции логгирования:
void logMessage (const char *str)
{
printf("%s", str);
}
Вызов
printf и ей подобных должен выглядеть именно так и никак иначе.Читать об уязвимостях printf.
Технологии мчатся вперед, а ошибки не меняются :)
16 марта 2008 г.
Проблема импорта в Mozilla Thunderbird
Столкнулся сегодня с проблемой импорта почтовых сообщений из Microsoft Outlook в Mozilla Thunderbird 2.0.0.12.
В процессе импорта вдруг возникают сообщения об ошибках, типа "Невозможно сохранить сообщение как черновик. Невозможно создать временный файл. Проверьте настройки временной папки." В результате из Outlook импортируется только часть сообщений.
Поиск в сети ничего не дал. С подобной проблемой люди уже сталкивались, но все эти проблемы относятся к версиям Thunderbird не выше 1.5 и связаны с тем, что старые версии Thunderbird очень плохо относились к именам временных папок которые содержали пробелы. Это не мой случай, потому что изменение имени временной папки ничего не дало — проблема осталась.
После трехчасовой возни я все-таки выяснил, в чем дело :) Оказалось, что Антивирус Касперского мешает нормальной работе программы импорта. Я не знаю почему это происходит, и почему Касперский ничего не сообщал о том, что он там что-то блокировал, но факт налицо — после приостановки работы Касперского процедура импорта выполнилась без единой ошибки.
В процессе импорта вдруг возникают сообщения об ошибках, типа "Невозможно сохранить сообщение как черновик. Невозможно создать временный файл. Проверьте настройки временной папки." В результате из Outlook импортируется только часть сообщений.
Поиск в сети ничего не дал. С подобной проблемой люди уже сталкивались, но все эти проблемы относятся к версиям Thunderbird не выше 1.5 и связаны с тем, что старые версии Thunderbird очень плохо относились к именам временных папок которые содержали пробелы. Это не мой случай, потому что изменение имени временной папки ничего не дало — проблема осталась.
После трехчасовой возни я все-таки выяснил, в чем дело :) Оказалось, что Антивирус Касперского мешает нормальной работе программы импорта. Я не знаю почему это происходит, и почему Касперский ничего не сообщал о том, что он там что-то блокировал, но факт налицо — после приостановки работы Касперского процедура импорта выполнилась без единой ошибки.
Подписаться на:
Сообщения (Atom)