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

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

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

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

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

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

wnd.style |= WS_VISIBLE;

вместо

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

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

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

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

#include <stddef.h>

//***************************

// Макрос для свойства

#define PROPERTY(ownertype, proptype, prop, getfunc, setfunc) \

class 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.)







Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх