среда, 20 января 2010 г.

error C2061: syntax error : identifier '_DebugHeapTag_func'

This compile error take place in debug mode only. If you got this compile error - explore your code find redefinitions for the "new" operator. Remove these redefinitions and compilation will succeed.

For example, in MFC application wizard insert following definition:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

Just remove these lines. That's all.

суббота, 19 декабря 2009 г.

Библиотека cURL и Gene6 FTP Server

Долгое время пользуюсь cURL и вот столкнулся с ситуацией, когда моё приложение оказалось неспособно залить файл на FTP-сервер, причём раньше таких проблем не было. Проявляется это только для сервера Gene6 FTP Server, а на других, имеющихся у меня, всё работает нормально.

После анализа ситуации выяснилось, что cURL, анализируя результат выполнения операции ожидает от сервера только один ответ, а Gene6 отвечает дважды. Вот лог:

(001517) 12/19/2009 2:25:07 PM - user (192.168.2.12) > APPE TPG.pdf
(001517) 12/19/2009 2:25:07 PM - user (192.168.2.12) > asked to upload 'TPG.pdf' in 'D:\ftp\user\test\ftp\' --> Access allowed.
(001517) 12/19/2009 2:25:07 PM - user (192.168.2.12) > 150 Data connection accepted from 192.168.2.12:3147; transfer starting for TPG.pdf.
(001517) 12/19/2009 2:25:07 PM - user (192.168.2.12) > started uploading 'TPG.pdf' in 'D:\ftp\user\test\ftp\'.
(001517) 12/19/2009 2:25:07 PM - user (192.168.2.12) > 150 APPE supported. Ready to append file "TPG.pdf" at offset 202400.
(001517) 12/19/2009 2:25:08 PM - user (192.168.2.12) > 226 File received ok.
(001517) 12/19/2009 2:25:08 PM - user (192.168.2.12) > finished uploading 'TPG.pdf' in 'D:\ftp\user\test\ftp\' - (00:00:02 - 197.656 KB - 98.828 KBytes/s).

Сервер сначала шлёт сообщение с кодом 150, а потом 226, но cURL уже отфильтровал ответ и интерпретировал его как "предыдущая команда завершилась с ошибкой". После чего передача данных прекращается.

Анализ кода ответа FTP-сервера находится в файле ftp.c (исходный код библиотеки cURL). в районе строки 3342.

if(!ftpc->dont_check) {
/* 226 Transfer complete, 250 Requested file action okay, completed. */
if((ftpcode != 226) && (ftpcode != 250)) {
failf(data, "server did not report OK, got %d", ftpcode);
result = CURLE_PARTIAL_FILE;
}
}

Нехитрая правка в этом месте помогает решить проблему:

if(!ftpc->dont_check) {
/* Some ftp servers send double response, so if we get response code equal to 150 - try to get response again. */
if(ftpcode == 150))
result = Curl_GetFTPResponse(&nread, conn, &ftpcode);

/* 226 Transfer complete, 250 Requested file action okay, completed. */
if((ftpcode != 226) && (ftpcode != 250)) {
failf(data, "server did not report OK, got %d", ftpcode);
result = CURLE_PARTIAL_FILE;
}
}

понедельник, 14 декабря 2009 г.

Проверка на существование таблиц и других объектов баз данных MS SQL

Проверка на существование таблиц, хранимых процедур, видов, функций и даже ключей основана на выборках из таблицы, а точнее вида (View), под названием sysobjects.

Вот пример функции, проверяющей существование таблицы в базе данных:

ALTER FUNCTION [dbo].[is_tbl_exists]
(
@tbl_name varchar(50)
)
RETURNS bit
AS
BEGIN
IF EXISTS(SELECT name FROM sysobjects WHERE name = N'' + @tbl_name AND xtype='U')
RETURN 1

RETURN 0
END

Если в условии выборки по столбцу xtype значение 'U' заменить на 'V' - получим проверку на существование вида

ALTER FUNCTION [dbo].[is_view_exists]
(
@view_name varchar(50)
)
RETURNS bit
AS
BEGIN
IF EXISTS(SELECT name FROM sysobjects WHERE name = N'' + @view_name AND xtype='V')
RETURN 1

RETURN 0
END

Далее, проверка на существование функций:

ALTER FUNCTION [dbo].[is_function_exists]
(
@function_name varchar(50)
)
RETURNS bit
AS
BEGIN
IF EXISTS(SELECT name FROM sysobjects WHERE name = N'' + @function_name AND xtype='FN')
RETURN 1

RETURN 0
END

Проверка на существование процедуры:

ALTER FUNCTION [dbo].[is_procedure_exists]
(
@procedure_name varchar(50)
)
RETURNS bit
AS
BEGIN
IF EXISTS(SELECT name FROM sysobjects WHERE name = N'' + @procedure_name AND xtype='P')
RETURN 1

RETURN 0
END

Конвертация даты в MS SQL из varchar в datetime и обратно

Иногда бывает необходимо реализовать конвертацию даты, записанной в виде строки в тип datetime или наоборот. В TSQL есть функция CONVERT, которая позволяет выполнить такое преобразование. Например, пользовательская функция преобразования значения типа datetime в строку вида YYYY-MM-DD HH:MM:SS, может выглядеть так:

CREATE FUNCTION [dbo].[dt_to_str]
(
@dt datetime
)
RETURNS varchar(19)
AS
BEGIN
RETURN CONVERT(varchar(19), @dt, 120)
END

Однако использовать CONVERT для преобразования строки в datetime не безопасно, особенно если планируется теражировать базу. Может возникнуть ситуация, когда это преобразование будет завершаться с ошибкой из-за неверного формата записи даты.
Более безопасный способ преобразования - использование функции dateadd. Известно, что datetime отсчитывается от 1 января 1900 года. Этой дате соответствует нулевое значение, проверьте:

SELECT cast(0 as datetime)

Соответственно, находя разницу с этой нулевой датой и прибавляя её к ней мы можем получить представление строки в виде даты типа datetime. Для начала простой пример:

ALTER FUNCTION [dbo].[make_date]
(
@day int,
@month int,
@year int
)
RETURNS datetime
AS
BEGIN
DECLARE @dt datetime
SET @dt = cast(0 as datetime)

-- прибавляем года
SELECT @dt = dateadd(year, (@year - 1900), @dt)
-- прибавляем месяцы
SELECT @dt = dateadd(month, (@month - 1), @dt)
-- прибавляем дни
SELECT @dt = dateadd(day, (@day - 1), @dt)

RETURN @dt
END

Эта функция получает в качестве целочисленных параметров день, месяц и год, а возвращает соответствующую им дату в формате datetime.

Теперь рассмотрим комбинированный пример со строками и целыми числами. Теперь дата и время будут представлены в виде длинного целого формата YYYYMMDDHHII. Функция преобразования будет выглядеть так:

ALTER FUNCTION [dbo].[int_to_datetime]
(
@val bigint
)
RETURNS datetime
AS
BEGIN
DECLARE @str varchar(12)
SET @str = CAST(@val AS varchar)

IF LEN(@str) < 12 -- Формат числа неверный
OR CAST(SUBSTRING(@str, 5, 2) AS int) > 12 -- Месяц неверный
OR CAST(SUBSTRING(@str, 7, 2) AS int) > 31 -- День
OR CAST(SUBSTRING(@str, 9, 2) AS int) > 23 -- Час
OR CAST(SUBSTRING(@str, 11, 2) AS int) > 60 -- Минута
RETURN NULL

DECLARE @dt datetime
SET @dt = cast(0 as datetime)

-- извлечение года
SELECT @dt = dateadd(year, (SUBSTRING(@str, 1, 4) - 1900), @dt)
-- извлечение месяца
SELECT @dt = dateadd(month, (SUBSTRING(@str, 5, 2) - 1), @dt)
-- извлечение дня
SELECT @dt = dateadd(day, (SUBSTRING(@str, 7, 2) - 1), @dt)
-- извлечение часа
SELECT @dt = dateadd(hour, CAST(SUBSTRING(@str, 9, 2) AS int), @dt)
-- извлечение минуты
SELECT @dt = dateadd(minute, CAST(SUBSTRING(@str, 11, 2) AS int), @dt)

RETURN @dt
END

Работает это преобразование быстро и вы не зависите от установок формата даты.

суббота, 26 сентября 2009 г.

Boost Test. Настройка и использование

Недавно в своём блоге я делал краткий обзор инструментов юнит-тестирования и отмечал наличие короткой справки для быстрого старта с библиотекой UnitTest++. Сегодня я хочу предложить вариант такой справки для Boost Test, чего вы не найдёте в официальной документации к этому инструменту. Описание ориентировано на использование в Windows с Visual Studio 2005/2008.

Для работы с примерами вам нужно скачать с сайта www.boost.org пакет библиотек (на текущий момент версии 1.39). Распакуйте скачанный архив и мы можем начинать.

Сборка библиотеки Boost.Test

Использовать Boost.Test можно в бинарном виде и в виде подключаемого заголовочного файла. Далее я буду описывать использование этой библиотеки в двоичном виде, подключаемой к тестовому проекту статически.

В корневой директории скачанного дистрибутива Boost найдите файл bootstrap.bat и выполните его. В результате будет подготовлена к использованию утилита сборки Boost. В той же директории, где хранится только что запущенный bat-файл, появится файл bjam.exe.

Теперь запустите из командной строки blam.exe с параметрами сборки Boost.Test:
bjam --build-dir=..\boosttest\build_dir toolset=msvc --libdir=..\boosttest\lib --includedir=..\boosttest\include --with-test variant=debug link=static threading=multi runtime-link=shared install

В той же папке, куда вы распаковали архив Boost будет создана папка boosttest, содержащая 3 папки:
  • build_dir, где хранятся промежуточные и результирующие файлы сборки Boost.Test
  • include, с заголовочными файлами всех утилит библиотеки Boost для использования в вашем проекте
  • lib, с lib-файлами для подключения к проекту
Создание тестового проекта
Далее я опишу создание тестового проекта с юнит-тестами. Пользуясь этим описанием вы сможете по аналогии использовать тесты в своём проекте.

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

Итак, создадим пустой проект:

New Project > Visual C++ > General > Empty Project

Сначала напишем простую программу умножающую 2 целых числа и отображающую их произведение.
Добавим в проект файл main.cpp со следующим содержанием:

//! main.cpp

#include <iostream>
#include "functions.h"

using namespace std;

void main()
{
cout << "Main Program" << endl;
cout << "5 * 4 = " << multiply(5, 4) << endl;
}
Также добавим файл с реализацией функции умножения:

//! functions.cpp
#include "functions.h"

int multiply(const int x, const int y)
{
return x * y;
}
И заголовочный файл с описанием функции умножения:

//! functions.h
int multiply(const int x, const int y);

Теперь добавим файлы с кодом тестирования. Сначала добавим файл maintest.cpp, в котором объявим константу BOOST_TEST_MAIN для того, чтобы Boost Test автоматически сгенерировал функцию main для тестов. Здесь же напишем первый тест, который всегда будет ошибочным. Я делаю это только для того, чтобы показать как использовать библиотеку для тестов, реализация которых находится в нескольких файлах.

//! maintest.cpp

#define BOOST_TEST_MAIN

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(SimpleTestInMainTestingModule)
{
BOOST_CHECK(1 == 2);
}

Теперь добавим ещё один файл functionsTest.cpp, который будет содержать тест для функции multiply из functions.cpp:

//! functionsTest.cpp

#include "functions.h"
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(Multiply)
{
BOOST_CHECK(multiply(4, 5) == 20);
}
Добавление тестовой конфигурации

Теперь необходимо решить проблему конфликта main, описанной в main.cpp и, той что будет сгенерирована Boost. Для этого добавим новую конфигурацию проекта для юнит-тестов, из сборки которой будет исключён модуль main.cpp, а в Debug и Release -конфигурациях нужно исключить из сборки mainTest.cpp

Чтобы добавить новую конфигурацию проекта в меню Build выберите Configuration Manager.... В появившемся диалоговом окне выберите в списке Active solution configuration пункт New. Введите имя новой конфигурации UnitTests и сохраните изменения.

Сделайте активной конфигурацию UnitTests. В контекстном меню файла main.cpp выберите Properties и на вкладке General для свойства Excluded from build установите значение Yes. Сделайте активным конфигурацию Debug и для файлов maintest.cpp и functionsTest.cpp также установите опцию исключения из сборки. Аналогично Debug настройте Release конфигурацию.

Теперь нужно настроить тестовую конфигурацию проекта. Снова сделайте активной конфигурацию UnitTests и в свойствах проекта для этой конфигурации выполните следующие настройки:

В настройках проекта C++ > General > Additional Include Directories вписать путь к заголовоным файлам boost.test

Linker > General > Additional Library Directories путь к папке с библиотекой libboost_unit_test_framework-vc90-mt-gd-1_39.lib

Linker > System > SubSystem выбрать из списка опцию Console (/SUBSYSTEM:CONSOLE)

Build Events > Post-Build Event > Command Line
"$(TargetDir)\$(TargetName).exe" --result_code=no --report_level=short

Build Events > Post-Build Event > Description
==== Run unit tests ====

Настроить вывод результатов можно иначе. Описание параметров, для управления выводом, приведено в официальной документации в разделе Runtime Parameters Reference.

Постройте и выполните проект в Debug или Release -конфигурациях. На экран будет выведено сообщение:

Main Program
5 * 4 = 20

Затем соберите проект в конфигурации UnitTests. В окне Output помимо информации о ходе компиляции будет что-то вроде этого:

==== Run unit tests ====
Running 2 test cases...
./maintest.cpp(13): error in "SimpleTestInMainTestingModule": check 1 == 2 failed
Test suite "Master Test Suite" failed with:
1 assertion out of 2 passed
1 assertion out of 2 failed
1 test case out of 2 passed
1 test case out of 2 failed

Если выполнить файл SingleProject.exe собранный в конфигурации UnitTests вы увидите примерно то же сообщение.
Если вы видите это сообщение, значит проект настроен правильно. Вы можете также скачать исходники проекта и поработать с ним.

Надеюсь эта информация будет полезна. Исправления и дополнения приветствуются!

четверг, 13 августа 2009 г.

Unit Testing Frameworks - Инструменты юнит-тестирования

Сегодня юнит-тестирование применяется во всех серьёзных софтверных компаниях и инструментов для проведения такого рода тестов появилось очень много. Microsoft даже включила собственный механизм юнит-тестирования .Net приложений в Visual Studio 2008 Pro. Но раз уж я больше специализируюсь на так называемом unmanaged C++ и более интересуюсь свободным ПО, то и писать буду про открытые инструменты тестирования C++ кода. Большое количество ссылок с описанием таких инструментов можно найти на странице C/C++ unit testing tools проекта www.opensourcetesting.org.

Благодаря многочисленным книгам по разработке ПО, среди которых такие бестселлеры, как "Рефакторинг" Мартина Фаулера, пожалуй, наиболее известный инструмент юнит-тестирования - это xUnit (cppUnit для C++, JUnit для Java, phpUnit для PHP, ...). Надо сказать, что написан он был прежде всего для Java (JUnit), а потом уже появились версии для других языков. Довольно хороший инструмент, но не самый лучший, на мой взгляд, именно для C++. Лично мне не нравится то, что при написании тестов приходится много писать служебного кода (наследовать от базового класса и реализовывать методы setup и tear down. Хочется минимум лишнего, компактнее код. Так как инструмент портирован с Java, он не использует многих возможностей C++.

Последнее время всё популярнее становятся фреймворки, построенные на макросах, среди которых Boost Test, googletest и UnitTest++. Фраймворки google и boost имеют больше возможностей, чем UnitTest++ и продолжают развиваться, но большое достоинство UnitTest++ - возможность быстрого старта, благодаря простоте и подробному руководству по установке для Visual Studio.

Я думаю, для пользователей Visual Studio, желающих познакомиться с юнит-тестированием идеально подойдёт UnitTest++. Чтобы начать работать с этим фреймворком достаточно скачать исходники, открыть солюшн в Visual Studio и запустить сборку. Готовый UnitTest++.vsnet2005.lib можно использовать в своём проекте. Для начинающих имеется пошаговое руководство использования UnitTest++, на примере тестового приложения показывающее как настраивать проект и как применять инструменты тестирования. Скорее всего вы легко сможете разобраться с UnitTest++ за один вечер и сразу начать его использовать. Для получения информации об использовании фреймворка имеется краткая справка. Справка действительно очень короткая - на одну страницу. С одной стороны так проще начать работать, не утруждая себя утомительным чтением, а с другой стороны, чтобы сделать что-то выходящее за рамки описанного, придётся смотреть исходные коды и гуглить.

Boost Test не имеет такой удобной краткой справки по развёртыванию и, если вы не знакомы с boost, придётся сначала прочесть документацию о том как скомпилировать библиотеку и в какой конфигурации (статической или динамической), затем перейти к справке по подключению библиотеки к проекту и уж только разобравшись с этим начинать работать. Но разобравшись вы получите мощный инструмент юнит-тестирования с хорошей документацией и обширными возможностями, очень гибкий и переносимый.

Документация по сборке библиотек boost приводится в руководстве Getting Started для Windows и Unix (Linux, MacOS). Скачать исходники можно там же с официального сайта.

В одной из следующих статей я подробно опишу процесс сборки и использования Boost Test в проекте Visual Studio.

Кроме перечисленных инструментов юнит-тестирования существуют и другие. Большой список бесплатных инструментов юнит-тестирования с описанием и ссылками вы можете найти на странице Open source C/C++ developer testing tools. На том же сайте есть ссылки на инструменты тестирования для других языков программирования.

вторник, 21 июля 2009 г.

Microsoft Terminology Translations - Перевод терминов Microsoft

При разработке пользовательского интерфейса всегда встаёт вопрос как правильно назвать элемент интерфеса. Особенно это важно, когда необходимо локализовать интерфейс.

К Microsoft можно относится по разному, но мне определённо нравится их дивиз "мы устанавливаем стандарт", который в полной мере можно отнести и к именованию элементов интерфейса в их программах. Узнав, какую функцию выполняет кнопка Edit в одной программе уже ожидаешь схожего поведения в другой. Это очень удобно. И если на своём родном языке и на английском мы уже привыки к каким-то шаблонам, то подобрать подходящее название на малознакомом языке не так то просто.
Microsoft открыто распространяет переводы наиболее часто используемых терминов на несколько языков мира. Например, со страницы Microsoft Terminology Translation Download можно скачать переводы терминов более чем на 40 языков. А здесь собраны ссылки на онлайн-ресурсы. Вот прямая ссылка на языковой портал Microsoft Language Portal где можно в онлайн переводить термины и смотреть как они используются в контексте различных приложений. Там же собраны ссылки на другие инструменты.