Отправляет email-рассылки с помощью сервиса Sendsay

Программирование на Visual С++

  Все выпуски  

Программирование на Visual С++ - No.46 (Свойства в С++, создание источника данных)


Служба Рассылок Subscribe.Ru проекта Citycat.Ru

ПРОГРАММИРОВАНИЕ НА VISUAL C++

Выпуск No. 46 от 27 мая 2001 г.

Приветствую вас, уважаемые подписчики!

Мой коллега Александр Шаргин решил присоединиться к дисскуссии по поводу свойств в С++:

/ / / / ОБРАТНАЯ СВЯЗЬ / / / / / / / / / / / / / / / / / / / / / /

Свойства "в стиле VB" невозможно заменить перегрузкой оператора присваивания и приведения типа. Если в классе содержится 10 свойств типа int, мы неизбежно заходим в тупик. Что касается полезности этой концепции, она признана многими разработчиками компонентно-ориентированных средств разработки (кроме VB есть Delphi и BCB, в которые свойства вводились отнюдь не только для поддержки COM, свойства есть в новом языке C# от Микрософт и т. д.). Конечно, свойства - не революция. Вместо них можно использовать пару методов Set/Get. Но они делают смысл происходящего в программе понятнее:

wnd.style |= WS_VISIBLE;

вместо

wnd.SetStyle(wnd.GetStyle() | WS_VISIBLE);

Кроме того, они развивают концепцию сокрытия деталей реализации от пользователя класса: для него свойство выглядит как обычная переменная-член, и он даже не догадывается, что для её реализации используются функции. Не секрет, что многие программисты не любят методы Set/Get и продолжают использовать открытые члены, несмотря на все призывы Страуструпа и иже с ним. Даже в книгах по программированию этот "неправильный" подход встречается сплошь и рядом. Свойства позволяют найти компромисс между этим чисто человеческим нежеланием и важными принципами ООП. В выигрыше оказываются все.

Что касается реализации свойств с С++, здесь перед нами встают проблемы. Если вопрос портирования прграммы для нас не актуален, и мы ограничиваемся VC, я не вижу смысла игнорировать его расширенные возможности. Использование __deсlspec(property) в этом случае является исключительно делом вкуса программиста. Принципиальных возражений против его использования нет.

А вот "самодельные" свойства приносят в жертву эффективность программы, и поэтому мы должны или отказаться от них, или улучшить, убрав лишние затраты. Вот пример возможного улучшения.

#include <stddef.h>

//***************************
// Макрос для свойства
#define PROPERTY(ownertype, proptype, prop, getfunc, setfunc)
\
class __property##prop
\
{
\
public:
\
    operator proptype()
\

{
\
        return ((ownertype *)((char *)this - offsetof(ownertype,
prop)))->getfunc(); \
    }
\
    void operator=(proptype data)
\

{
\
        ((ownertype *)((char *)this - offsetof(ownertype,
prop)))->setfunc(data);    \
    }
\
};
\
friend __property##prop;
\
__property##prop prop

//***************************
// Пример использования
class CValue
{
private:
    char m_value;

public:
    int get_Value()
    {
        return m_value;    // Или более сложная логика
    }
    void put_Value(int value)
    {
        m_value = value;     // Или более сложная логика
    }

    // Свойство Value типа int, использующее функции
    // get_Value и put_Value класса CValue
    PROPERTY(CValue, int, Value, get_Value, put_Value);
};

int main(int argc, char* argv[])
{
CValue val;

/*
Здесь вызывается оператор присваивания переменной-члена val.Value,
и, следовательно, функция val.put_Value()
*/
val.Value = 50;

/*
Здесь вызывается оператор приведения типа переменной-члена val.Value,
и, следовательно, функция val.get_Value()
*/
int z = val.Value;

return 0;
}

В этом случае ликвидируются как временные затраты (все функции класса __property##prop являются встроенными), так и затраты по памяти (к сожалению, только теоретически, так как VC "не умеет" создавать объекты класса нулевой длины). Кроме того, следует заметить, что полученное свойство не является полным эквивалентом __declspec. Например, нельзя написать

val.Value += 50;

Этот недостаток присущ и реализации в выпуске 43. Чтобы устранить его, придётся перегружать множество операторов, а потом, возможно, отлавливать множество тонких ошибок. Вот почему я считаю, что "самодельные" свойства представляют скорее теоретический интерес. Это, однако, не повод провозглашать ненужной саму концепцию свойств, аргументируя это мощью и могуществом языка C++, который в них не нуждается.
- Александр Шаргин

/ / / / ВОПРОС-ОТВЕТ / / / / / / / / / / / / / / / /

Как программно создать источник данных?

Для этой цели служит функция SQLConfigDataSource(). Она позволяет создать пользовательский или системный источник данных (DSN - DataSource Name). Эта же функция позволяет модифицировать или удалить DSN.

BOOL SQLConfigDataSource(
     HWND      hwndParent,
     WORD      fRequest,
     LPCSTR    lpszDriver,
     LPCSTR    lpszAttributes);

Здесь hwndParent - хэндл окна, которому будут направляться сообщения об ошибках (в случае использования NULL эти сообщения будут подавляться), fRequest - тип выполняемого действия (к примеру, ODBC_ADD_DSN - добавить пользовательский DSN, ODBC_ADD_SYS_DSN - добавить системный DSN), lpszDriver - точное имя драйвера ODBC, так, как оно выглядит в диалоге настройки ODBC DSN, например "Microsoft Access Driver (*.mdb)" или "SQL Server". Строка lpszAttributes содержит основные параметры подключения к источнику данных:

  • DSN - название создаваемого источника данных
  • UID - имя пользователя
  • DATABASE - имя базы данных
  • PWD - пароль для подключения

Параметры разделены между собой символом '\0', конец строки отмечается дополнительным символом '\0'.

DSN=CustomDsn\0UID=username\0PWD=password\0DATABASE=CustomDataBase\0\0

Обязательным является имя DSN. Все остальные параметры могут быть запрошены при подключении к источнику данных. Хотя различные драйверы ODBC в этом отношении могут вести себя по-разному - например, для драйвера MS SQLServer обязательным параметром также является и имя сервера.

///////////////////////////////////////////////////////////
////  Пример создания источника данных для 
//  ODBC драйвера для Microsoft Excel 97
//
#include <windows.h>
#include <odbcinst.h>
#pragma comment(lib, "odbccp32")
#pragma comment(lib, "user32")

void main()
{
    char* driver = "Microsoft Excel Driver (*.xls)";
    char* params = "DSN=MyTable\0DefaultDir=D:\\Document\0""DBQ=D:\\Document\\MyTable.xls\0";

    // Создадим пользовательский DSN
    SQLConfigDataSource(NULL, ODBC_ADD_DSN, driver, params);
}

Надеюсь, для Вас не будет слишком большим сюрпризом узнать, что всю информацию об источниках данных и драйверах ODBC Windows хранит в реестре. А если быть совсем точным, то в ключах


HKEY_CURRENT_USER\Software\ODBC\ODBC.INI      (пользовательские DSN)
HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBC.INI     (системные DSN)
HKEY_LOCAL_MACHINE\SOFTWARE\ODBC\ODBCINST.INI (драйверы ODBS)

Информацию о том, как правильно заполнять строку params, можно извлечь самостоятельно, создавая при помощи ODBC-администратора источники данных для различных драйверов Вашей системы и анализируя состав параметров и присвоенные им значения ключей реестра, относящихся к созданным источникам. Надеюсь также, что не сильно шокирую Вас, если скажу, что приведенный выше пример был написан именно так.

/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /

Всего доброго и до встречи через неделю!

Алекс Jenter   jenter@rsdn.ru
Красноярск, 2001. Рассылка является частью проекта RSDN.

Предыдущие выпуски     Статистика рассылки


RLE Banner Network

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться Relayed by Corbina
Рейтингуется SpyLog

В избранное