Приветствую вас, уважаемые подписчики!
Мой коллега Александр Шаргин решил присоединиться к дисскуссии по поводу
свойств в С++:
/ / / / ОБРАТНАЯ СВЯЗЬ / / / / / / / / / / / / / / / / / / / / / /
Свойства "в стиле 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) в этом случае является исключительно делом вкуса
программиста. Принципиальных возражений против его использования нет.
А вот "самодельные" свойства приносят в жертву эффективность программы, и
поэтому мы должны или отказаться от них, или улучшить, убрав лишние затраты.
Вот пример возможного улучшения.
В этом случае ликвидируются как временные затраты (все функции класса
__property##prop являются встроенными), так и затраты по памяти (к
сожалению, только теоретически, так как VC "не умеет" создавать объекты
класса нулевой длины). Кроме того, следует заметить, что полученное свойство
не является полным эквивалентом __declspec. Например, нельзя написать
val.Value += 50;
Этот недостаток присущ и реализации в выпуске 43. Чтобы устранить его,
придётся перегружать множество операторов, а потом, возможно, отлавливать
множество тонких ошибок. Вот почему я считаю, что "самодельные" свойства
представляют скорее теоретический интерес. Это, однако, не повод
провозглашать ненужной саму концепцию свойств, аргументируя это мощью и
могуществом языка C++, который в них не нуждается.
/ / / / ВОПРОС-ОТВЕТ / / / / / / / / / / / / / / / /
Как программно создать источник данных?
#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;
}
- Александр Шаргин
Для этой цели служит функция 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 содержит основные параметры подключения к источнику данных:
Параметры разделены между собой символом '\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.