2Yocto,
Спасибо за реакцию. Приятно, что не перевелись ещё в Канаде
грамотные товарищи, приехавшие "от сюда".
MC++ - это просто затычка для тех случаев, когда нужно использовать предоставляемую фрэймворком логику и в то же время напрямую обращаться к функциям native API.
Хотя при большом желании на нём можно создавать что-либо сложное тоже.
За последние пару лет я встретился с уймой ситуаций, когда нужен был именно MC++.
1) О Microsofte и о С++(если Вы об этом, конечно). Я бы разделил две вещи - качество и возможности компилятора с одной стороны, и те библитотеки (качество библиотеки, её "вылизанность", совместимость с другими библиотеками) с другой.
Многое из того, что Борланд пропускает мимо ушей, MS делает более корректно. Не знаю, может это и дурной тон, и где-то пустая трата времени, но "отмоделировав" что-либо в Борланде, "релиз" я переписываю всёж в MS. Разумеется, когда это возможно.
2) Об API. Вы пишете о "напрямую обращаться к функциям native API". Я понимаю, что этого часто просто не избежать. Однако, я стараюсь
сделать такую работу
один раз, например, "вылизав" соответствующие
алгоритмы и их реализацию, далее обернуть ЭТО в более высокоуровневые классы, и пользоваться в "текучке" только этими высокоуровневыми обёртками.
Постоянно и непосредственно обращаться к Win API, или к её части - на мой взгляд - дурной тон в программировании. Это касается не только тех случаев, когда программер обращается к Win API при программировании каркаса могутной архитектуры, но и более простых случаев, когда предоставляемые «стандартными» библиотеками классы, хороши, но чуть-чуть не функциональны (Как невеста – хороша, но чуть-чуть беременна).
Например, в общем-то неплохой класс MFC CDatabase (по сравнению с дубовыми сокетами борланда) не позволяет сделать совершенно тривиальную вещь - задать уровень изоляции транзакции. Для решения задачи можно, конечно, по доступному хэндлу (дескриптору) ODBC сделать это при помощи API ODBC, но проще ЭТО
один раз обернуть, и затем использовать повсеместно. Т.е. вместо использования стандартных классов + стандартного API - более разумно, на мой взгляд, использовать API обёрток, типа:
Код: Выделить всё
class CI_ODBCdb: public XXXX
{
private:
CDatabase* Database;
…
bool TransactionPending;
public:
void __stdcall TransIsolation( LPCTSTR type );
….
};
//---------------------------------------------------------------------------
void __stdcall CI_ODBCdb::TransIsolation( LPCTSTR type )
{
if ( !_tcsicmp( type,_TEXT("DIRTYREAD")) )
SQLSetConnectAttr(Database->m_hdbc, SQL_ATTR_TXN_ISOLATION,
(SQLPOINTER) SQL_TXN_READ_UNCOMMITTED,
SQL_IS_INTEGER);
else if ( !_tcsicmp( type,_TEXT("READCOMMITTED")) )
SQLSetConnectAttr(Database->m_hdbc, SQL_ATTR_TXN_ISOLATION,
(SQLPOINTER) SQL_TXN_READ_COMMITTED,
SQL_IS_INTEGER);
else if ( !_tcsicmp( type,_TEXT("REPEATABLEREAD")) )
SQLSetConnectAttr(Database->m_hdbc, SQL_ATTR_TXN_ISOLATION,
(SQLPOINTER) SQL_TXN_REPEATABLE_READ,
SQL_IS_INTEGER);
}
//---------------------------------------------------------------------------
Это только пример для пояснения моей мысли.
Да, организовывать взаимодействие managed и unmanaged частей так, как было предложено (при динамической подгрузке) - технически выполнимо, но впоследстии может оказаться большой головной болью.
Пока, что это и не удаётся сделать эфективно(простыми средствами). Компилятор упорно подставляет соглашения о вызовах __clrcall вместо __stdcall. По-своему он (компилятор) прав. Не удаётся сделать так, чтобы вызывающий софт мог использовать соглашения паскаль (как для ВинАпи).
Мне кажется, более разумным было бы пользоваться стандартными протоколами, такими, как COM….
Что касается COM/DCOM – это сделали. Т.е в техзадании указано, что разработчик клиентского ПО может использовать COM. При этом он должен использовать следующий минимальный «набор» интерфейсов и их методов объектов доступа к данным сервера приложений:
Код: Выделить всё
// file dcomsrvquery.idl
[
uuid(D75D367B-D9CF-4B9A-BE7F-97A7D6A6D13B),
version(1.0),
helpstring("dcomsrvquery Data Access Library")
]
library dcomsrvquery
{
importlib("stdole2.tlb");
[
uuid(A72B9B21-0782-41A2-9753-6D68CD9FDE69),
version(1.0),
helpstring("Interface for CoDatabase Object"),
oleautomation
]
interface ICDatabase: IUnknown
{
[ propget, id(0x00000001) ]
HRESULT _stdcall InTransaction([out, retval] int * Value );
[ propget, id(0x00000002) ]
HRESULT _stdcall Database([out, retval] long * Value );
[ id(0x00000003) ]
HRESULT _stdcall Connection([in] BSTR connstr );
[ id(0x00000004) ]
long _stdcall Open( void );
[ id(0x00000005) ]
HRESULT _stdcall Close( void );
[ id(0x00000006) ]
HRESULT _stdcall Delete( void );
[ id(0x00000007) ]
long _stdcall StartTransaction( void );
[ id(0x00000008) ]
HRESULT _stdcall Commit( void );
[ id(0x00000009) ]
HRESULT _stdcall Rollback( void );
[ id(0x0000000A) ]
HRESULT _stdcall TransIsolation([in] BSTR transtype );
[ id(0x0000000B) ]
long _stdcall Initialize([in] BSTR type );
[ propget, id(0x0000000C) ]
HRESULT _stdcall ErrorMessage([out, retval] BSTR * Value );
};
[
uuid(201111C0-F20C-4EC1-AACA-89B8308D7E94),
version(1.0),
helpstring("CoDatabase data access object")
]
coclass CoDatabase
{
[default] interface ICDatabase;
};
[
uuid(6E03F4C1-83D4-424D-9BD2-7123514FECB2),
version(1.0),
helpstring("Interface for CoQuery Object"),
oleautomation
]
interface ICQuery: IUnknown
{
[ propget, id(0x00000001) ]
HRESULT _stdcall IntegerField([in] BSTR fieldname, [out, retval] int * Value );
[ propget, id(0x00000002) ]
HRESULT _stdcall FloatField([in] BSTR fieldname, [out, retval] float * Value );
[ propget, id(0x00000003) ]
HRESULT _stdcall StringField([in] BSTR fieldname, [out, retval] BSTR * Value );
[ propget, id(0x00000004) ]
HRESULT _stdcall DoubleField([in] BSTR fieldname, [out, retval] double * Value );
[ propget, id(0x00000005) ]
HRESULT _stdcall IsEof([out, retval] int * Value );
[ id(0x00000006) ]
long _stdcall Open([in] BSTR sqlstr );
[ id(0x00000007) ]
HRESULT _stdcall Connection([in] BSTR constr );
[ id(0x00000008) ]
long _stdcall Exec([in] BSTR sqlstr );
[ id(0x00000009) ]
HRESULT _stdcall First( void );
[ id(0x0000000A) ]
HRESULT _stdcall Next( void );
[ id(0x0000000B) ]
HRESULT _stdcall Close( void );
[ id(0x0000000C) ]
HRESULT _stdcall FreeConnections( void );
[ id(0x0000000D) ]
HRESULT _stdcall Delete( void );
[ id(0x0000000E) ]
long _stdcall Initialize([in] BSTR type );
[ id(0x0000000F) ]
HRESULT _stdcall Database([in] long db );
[ propget, id(0x00000010) ]
HRESULT _stdcall ErrorMessage([out, retval] BSTR * Value );
};
[
uuid(1016FEAD-C5C2-44A1-B150-B9572F0FD7FA),
version(1.0),
helpstring("CoQuery")
]
coclass CoQuery
{
[default] interface ICQuery;
};
};
// end of file dcomsrvquery.idl
Это минимальный набор объектов, интерфейсов их методов и пропертей.
Самое главное в том, что если разработчик клиентского ПО сочтёт, что существующий функционал недостаточный, то он будет согласовывать это с разработчиками ядра СЕРВЕРА ПРИЛОЖЕНИЙ. Т.е того, что составляет ПО промежуточного слоя. Мне чего-то сильно показалось, что некоторые товарищи не очень ориентируются, что такое «трёхзвенная архитектура» и middleware-решения. Поскольку и они тоже будут читать мой пост, то выглядит это так:
Клиентское ПО (запрос к серверу приложений) -> Сервер приложений (запрос к RDBMS) -> RDBMS.
Т.е. при использовании такой архитектуры клиентов разных типов может быть и сто, а сервер приложений и его API – один. Поэтому при решении задач модернизации и масштабирования придётся
перекомпилировать только один СЕРВЕР ПРИЛОЖЕНИЙ, а не всех клиентов. Этого, как раз твои заокеанские коллеги и «недошурупили». Хотя это обычная архитектура, которая используется повсеместно в сколь-нибудь серьёзных дорогостоящих информационных системах.
….или же сетевыми транспортами напрямую….
Да, это интересно,
но только мне. Подрядчиками (и ядра middleware сервера приложений и собственно клиентского ПО для конечного пользователя) будут Российские фирмы. Профессиональных и клеаренс фирм-разработчиков, доступных из нашего региона не так много. То барахло, что есть постоянно под рукой понятия не имеет о программировании серверов с использованием сокетов или MSRPC. Если честно сказать, то мой личный (очень и очень небольшой в RPC) опыт программирования даже простейших задач показывает, что программирование MSRPC напрямую может быть очень тяжёлой задачей для здешних программеров. А ведь в этом случае ещё и протокол прикладного уровня надо будет разрабатывать (между клиентским ПО и сервером приложений).
Или, уж если очень хочеться, построить совместные модули, как статически линкуемые библиотеки, что, на мой взгляд, содержит потенциальные проблемы в будущем.
Об этом не может быть и речи. Только позднее связывание. Иначе всё теряет смысл.
Для больших проектов для межпрограммного общения можно даже использовать SOAP или raw formatters, четкое разграничение по границе функциональности может дать более выгод даже в сравнении с накладными расходами. Имеется в виду прозрачность кода, его модульность и более лёгкое масштабирование.
Опять-таки, это очень интересно
лично для меня. Но это можно «доводить» до уровня пожеланий в техзадании только тогда, когда сам разберусь досконально, и когда нашинские программеры до этого вырастут. Сейчас это поставило бы под удар проект, привело бы к его затягиванию. В общем, рискованно пока, как и в случае создания серверов «с нуля» с использованием сокетов или RPC (см.выше).
PS.
Та «трёхзвенная» архитектура, о которой я постил – это хорошо знакомо всем.
Ну, а если кто из профи не владеет – подскажем.

) Поэтому и ставка сделана на middleware-ное решение.
Вместе с техзаданием мы распространяем CD-диск и примерами написания клиентского и серверного ПО.