Monday, June 11, 2007

How to write BOM(Byte of Marker) into file using fstream

The Byte Order Marker (BOM) is Unicode character U+FEFF. (It can also represent a Zero Width No-break Space.) The code point U+FFFE is illegal in Unicode, and should never appear in a Unicode character stream. Therefore the BOM can be used in the first character of a file (or more generally a string), as an indicator of endian-ness. With UTF-16, if the first character is read as bytes FE FF then the text has the same endian-ness as the machine reading it. If the character is read as bytes FF FE, then the endian-ness is reversed and all 16-bit words should be byte-swapped as they are read-in. In the same way, the BOM indicates the endian-ness of text encoded with UTF-32.

Note that not all files start with a BOM however. In fact, the Unicode Standard says that text that does not begin with a BOM MUST be interpreted in big-endian form.

The character U+FEFF also serves as an encoding signature for the Unicode Encoding Forms. The table shows the encoding of U+FEFF in each of the Unicode encoding forms. Note that by definition, text labeled as UTF-16BE, UTF-32BE, UTF-32LE or UTF-16LE should not have a BOM. The endian-ness is indicated in the label.

For text that is compressed with the SCSU (Standard Compression Scheme for Unicode) algorithm, there is also a recommended signature.

Encoding Form

BOM Encoding

UTF-8

EF BB BF

UTF-16
(big-endian)

FE FF

UTF-16
(little-endian)

FF FE

UTF-16BE, UTF-32BE
(big-endian)

No BOM!

UTF-16LE, UTF-32LE
(little-endian)

No BOM!

UTF-32
(big-endian)

00 00 FE FF

UTF-32
(little-endian)

FF FE 00 00

SCSU
(compression)

0E FE FF

 

 

Solutions:

 

Case 1: using ANSI std::ofstream:

 

wchar_t BOM = 0xFEFF;
std::ofstream outFile("filename.dat", std::ios::out | std::ios::binary);
outfile.write((char *) &BOM,sizeof(wchar_t));

 

Case 2: using ANSI std::wofstream:

 

  const wchar_t BOM = 0xFEFF;

  const char *fname = "abc.txt";

  std::wofstream wfout;

  wfout.open(fname,ios_base::binary);
 

   //S1:

   testFile << BOM;

 

   //S2:

   //testFile.put(BOM);

 

How to write file in unicode using std::fstream?

If you try to use fstream to write file in Unicode like the following style, you will got failed.

 

tofstream testFile( "test.txt" ) ;

testFile << _T("ABC") ;

you would expect the above code to produce a 3-byte file when compiled using single-byte characters and a 6-byte file when using double-byte. Except you don't. You get a 3-byte file for both. WTH is going on?!

It turns out that the C++ standard dictates that wide-streams are required to convert double-byte characters to single-byte when writing to a file. So in the example above, the wide string L"ABC" (which is 6 bytes long) gets converted to a narrow string (3 bytes) before it is written to the file. And if that wasn't bad enough, how this conversion is done is implementation-dependent.

I haven't been able to find a definitive explanation of why things were specified like this. My best guess is that a file, by definition, is considered to be a stream of (single-byte) characters and allowing stuff to be written 2-bytes at a time would break that abstraction. Right or wrong, this causes serious problems. For example, you can't write binary data to a wofstream because the class will try to narrow it first (usually failing miserably) before writing it out.

 

 

Solution 1: Use fstream::Write()

 

void Func10()

{

            wstringstream wss;

            wss << L"hello" << L"你好" <<L"test";

            char* st = (char*)wss.str().c_str();   //importance!

 

            fstream  m_ofs;

            m_ofs.open(_T("c:\\AllElem.txt"),ios_base::binary | ios_base::out );

 

            m_ofs.write(st, 100);

            m_ofs.close();

}

 

Solution 2: write a new codecvt-derived class that converts wchar_ts to wchar_ts (i.e. do nothing) and attach it to the wofstream object.

http://www.codeproject.com/vcpp/stl/upgradingstlappstounicode.asp

 

#include <locale>

// nb: MSVC6+Stlport can't handle "std::"

// appearing in the NullCodecvtBase typedef.

using std::codecvt ;

typedef codecvt < wchar_t , char , mbstate_t > NullCodecvtBase ;

 

class NullCodecvt

    : public NullCodecvtBase

{

 

public:

    typedef wchar_t _E ;

    typedef char _To ;

    typedef mbstate_t _St ;

 

    explicit NullCodecvt( size_t _R=0 ) : NullCodecvtBase(_R) { }

 

protected:

    virtual result do_in( _St& _State ,

                   const _To* _F1 , const _To* _L1 , const _To*& _Mid1 ,

                   _E* F2 , _E* _L2 , _E*& _Mid2

                   ) const

    {

        return noconv ;

    }

    virtual result do_out( _St& _State ,

                   const _E* _F1 , const _E* _L1 , const _E*& _Mid1 ,

                   _To* F2, _E* _L2 , _To*& _Mid2

                   ) const

    {

        return noconv ;

    }

    virtual result do_unshift( _St& _State ,

            _To* _F2 , _To* _L2 , _To*& _Mid2 ) const

    {

        return noconv ;

     }

    virtual int do_length( _St& _State , const _To* _F1 ,

           const _To* _L1 , size_t _N2 ) const _THROW0()

    {

        return (_N2 < (size_t)(_L1 - _F1)) ? _N2 : _L1 - _F1 ;

    }

    virtual bool do_always_noconv() const _THROW0()

    {

        return true ;

    }

    virtual int do_max_length() const _THROW0()

    {

        return 2 ;

    }

    virtual int do_encoding() const _THROW0()

    {

        return 2 ;

    }

};

 

#define IMBUE_NULL_CODECVT( outputFile ) \

{ \

            NullCodecvt* pNullCodecvt = new NullCodecvt ; \

            locale loc = locale::classic() ; \

            loc._Addfac( pNullCodecvt , NullCodecvt::id, NullCodecvt::_Getcat() ) ; \

            (outputFile).imbue( loc ) ; \

}

 

 

tofstream testFile ;

IMBUE_NULL_CODECVT( testFile ) ;

testFile.open( "test.txt" , ios::out | ios::binary ) ;

testFile << _T("ABC") ;

 

 

BTW: if you got the following compile error in VC 2005( I got itJ ):

- error C2661: 'std::locale::facet::operator new' : no overloaded function

takes 3 arguments

 

Solution:
Look for the following line in your code somewhere:
 
#define new DEBUG_NEW
 
MFC wizard puts it in. It messes with class-specific operator new(). It
looks like facet class now provides its own operator new. 
 
Comment it will fix your problem.

 

Saturday, June 2, 2007

ADO Events Model Example (VC++)


ADO Events Model Example (VC++)


The Visual C++ section of ADO Event Instantiation by Language gives a general description of how to instantiate the ADO event model. The following is a specific example of instantiating the event model within the environment created by the #import directive.


The general description uses adoint.h as a reference for method signatures. However, a few details in the general description change slightly as a result of using the #import directive:



  • The #import directive resolves typedef's, and method signature data types and modifiers to their fundamental forms.

  • The pure virtual methods that must be overwritten are all prefixed by "raw_".


Some of the code simply reflects coding style.



  • The pointer to IUnknown used by the Advise method is obtained explicitly with a call to QueryInterface.

  • You don't need to explicitly code a destructor in the class definitions.

  • You may want to code more robust implementations of QueryInterface, AddRef, and Release.

  • The __uuidof() directive is used extensively to obtain interface IDs.


Finally, the example contains some working code.



  • The example is written as a console application.

  • You should insert your own code under the comment, "// Do some work".

  • All the event handlers default to doing nothing, and canceling further notifications. You should insert the appropriate code for your application, and allow notifications if required.



// eventmodel.cpp : Defines the entry point for the console application.
//

#import "msado15.dll" \
no_namespace rename("EOF", "EndOfFile")
#include <comdef.h>
#include <stdio.h>

//----The Connection events----------------------------------------------

class CConnEvent : public ConnectionEventsVt
{
private:
ULONG m_cRef;
public:
CConnEvent() { m_cRef = 0; };
~CConnEvent() {};


STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);

STDMETHODIMP raw_InfoMessage(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection);

STDMETHODIMP raw_BeginTransComplete(
LONG TransactionLevel,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection);

STDMETHODIMP raw_CommitTransComplete(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection);

STDMETHODIMP raw_RollbackTransComplete(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection);

STDMETHODIMP raw_WillExecute(
BSTR *Source,
CursorTypeEnum *CursorType,
LockTypeEnum *LockType,
long *Options,
EventStatusEnum *adStatus,
struct _Command *pCommand,
struct _Recordset *pRecordset,
struct _Connection *pConnection);

STDMETHODIMP raw_ExecuteComplete(
LONG RecordsAffected,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Command *pCommand,
struct _Recordset *pRecordset,
struct _Connection *pConnection);

STDMETHODIMP raw_WillConnect(
BSTR *ConnectionString,
BSTR *UserID,
BSTR *Password,
long *Options,
EventStatusEnum *adStatus,
struct _Connection *pConnection);

STDMETHODIMP raw_ConnectComplete(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection);

STDMETHODIMP raw_Disconnect(
EventStatusEnum *adStatus,
struct _Connection *pConnection);
};

//-----The Recordset events----------------------------------------------

class CRstEvent : public RecordsetEventsVt
{
private:
ULONG m_cRef;
public:
CRstEvent() { m_cRef = 0; };
~CRstEvent() {};

STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);

STDMETHODIMP raw_WillChangeField(
LONG cFields,
VARIANT Fields,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset);

STDMETHODIMP raw_FieldChangeComplete(
LONG cFields,
VARIANT Fields,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset);

STDMETHODIMP raw_WillChangeRecord(
EventReasonEnum adReason,
LONG cRecords,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset);

STDMETHODIMP raw_RecordChangeComplete(
EventReasonEnum adReason,
LONG cRecords,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset);

STDMETHODIMP raw_WillChangeRecordset(
EventReasonEnum adReason,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset);

STDMETHODIMP raw_RecordsetChangeComplete(
EventReasonEnum adReason,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset);

STDMETHODIMP raw_WillMove(
EventReasonEnum adReason,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset);

STDMETHODIMP raw_MoveComplete(
EventReasonEnum adReason,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset);

STDMETHODIMP raw_EndOfRecordset(
VARIANT_BOOL *fMoreData,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset);

STDMETHODIMP raw_FetchProgress(
long Progress,
long MaxProgress,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset);

STDMETHODIMP raw_FetchComplete(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset);
};


//-----Implement each connection method----------------------------------

STDMETHODIMP CConnEvent::raw_InfoMessage(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CConnEvent::raw_BeginTransComplete(
LONG TransactionLevel,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CConnEvent::raw_CommitTransComplete(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CConnEvent::raw_RollbackTransComplete(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CConnEvent::raw_WillExecute(
BSTR *Source,
CursorTypeEnum *CursorType,
LockTypeEnum *LockType,
long *Options,
EventStatusEnum *adStatus,
struct _Command *pCommand,
struct _Recordset *pRecordset,
struct _Connection *pConnection)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CConnEvent::raw_ExecuteComplete(
LONG RecordsAffected,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Command *pCommand,
struct _Recordset *pRecordset,
struct _Connection *pConnection)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CConnEvent::raw_WillConnect(
BSTR *ConnectionString,
BSTR *UserID,
BSTR *Password,
long *Options,
EventStatusEnum *adStatus,
struct _Connection *pConnection)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CConnEvent::raw_ConnectComplete(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Connection *pConnection)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CConnEvent::raw_Disconnect(
EventStatusEnum *adStatus,
struct _Connection *pConnection)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};


//-----Implement each recordset method-----------------------------------

STDMETHODIMP CRstEvent::raw_WillChangeField(
LONG cFields,
VARIANT Fields,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CRstEvent::raw_FieldChangeComplete(
LONG cFields,
VARIANT Fields,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CRstEvent::raw_WillChangeRecord(
EventReasonEnum adReason,
LONG cRecords,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CRstEvent::raw_RecordChangeComplete(
EventReasonEnum adReason,
LONG cRecords,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CRstEvent::raw_WillChangeRecordset(
EventReasonEnum adReason,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CRstEvent::raw_RecordsetChangeComplete(
EventReasonEnum adReason,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CRstEvent::raw_WillMove(
EventReasonEnum adReason,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CRstEvent::raw_MoveComplete(
EventReasonEnum adReason,
struct Error *pError,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CRstEvent::raw_EndOfRecordset(
VARIANT_BOOL *fMoreData,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CRstEvent::raw_FetchProgress(
long Progress,
long MaxProgress,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};

STDMETHODIMP CRstEvent::raw_FetchComplete(
struct Error *pError,
EventStatusEnum *adStatus,
struct _Recordset *pRecordset)
{
*adStatus = adStatusUnwantedEvent;
return S_OK;
};


//-----Implement QueryInterface, AddRef, and Release---------------------

STDMETHODIMP CRstEvent::QueryInterface(REFIID riid, void ** ppv)
{
*ppv = NULL;
if (riid == __uuidof(IUnknown) ||
riid == __uuidof(RecordsetEventsVt)) *ppv = this;
if (*ppv == NULL)
return ResultFromScode(E_NOINTERFACE);
AddRef();
return NOERROR;
}
STDMETHODIMP_(ULONG) CRstEvent::AddRef(void) { return ++m_cRef; };
STDMETHODIMP_(ULONG) CRstEvent::Release()
{
if (0 != --m_cRef) return m_cRef;
delete this;
return 0;
}

STDMETHODIMP CConnEvent::QueryInterface(REFIID riid, void ** ppv)

{
*ppv = NULL;
if (riid == __uuidof(IUnknown) ||
riid == __uuidof(ConnectionEventsVt)) *ppv = this;
if (*ppv == NULL)
return ResultFromScode(E_NOINTERFACE);
AddRef();
return NOERROR;
}
STDMETHODIMP_(ULONG) CConnEvent::AddRef() { return ++m_cRef; };
STDMETHODIMP_(ULONG) CConnEvent::Release()
{
if (0 != --m_cRef) return m_cRef;
delete this;
return 0;
}

//-----Write your main block of code-------------------------------------

int main(int argc, char* argv[])
{
HRESULT hr;
DWORD dwConnEvt;
DWORD dwRstEvt;
IConnectionPointContainer *pCPC = NULL;
IConnectionPoint *pCP = NULL;
IUnknown *pUnk = NULL;
CRstEvent *pRstEvent = NULL;
CConnEvent *pConnEvent= NULL;
int rc = 0;
_RecordsetPtr pRst;
_ConnectionPtr pConn;

::CoInitialize(NULL);

hr = pConn.CreateInstance(__uuidof(Connection));
if (FAILED(hr)) return rc;

hr = pRst.CreateInstance(__uuidof(Recordset));
if (FAILED(hr)) return rc;

// Start using the Connection events

hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
(void **)&pCPC);
if (FAILED(hr)) return rc;
hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
pCPC->Release();
if (FAILED(hr)) return rc;

pConnEvent = new CConnEvent();
hr = pConnEvent->QueryInterface(__uuidof(IUnknown), (void **) &pUnk);
if (FAILED(hr)) return rc;
hr = pCP->Advise(pUnk, &dwConnEvt);
pCP->Release();
if (FAILED(hr)) return rc;

// Start using the Recordset events

hr = pRst->QueryInterface(__uuidof(IConnectionPointContainer),
(void **)&pCPC);
if (FAILED(hr)) return rc;
hr = pCPC->FindConnectionPoint(__uuidof(RecordsetEvents), &pCP);
pCPC->Release();
if (FAILED(hr)) return rc;

pRstEvent = new CRstEvent();
hr = pRstEvent->QueryInterface(__uuidof(IUnknown), (void **) &pUnk);
if (FAILED(hr)) return rc;
hr = pCP->Advise(pUnk, &dwRstEvt);
pCP->Release();
if (FAILED(hr)) return rc;

// Do some work

pConn->Open("dsn=Pubs;", "MyUserName", "MyPassword", adConnectUnspecified);
pRst->Open("SELECT * FROM authors", (IDispatch *) pConn,
adOpenStatic, adLockReadOnly, adCmdText);
pRst->MoveFirst();
while (pRst->EndOfFile == FALSE)
{
wprintf(L"Name = '%s'\n", (wchar_t*)
((_bstr_t) pRst->Fields->GetItem("au_lname")->Value));
pRst->MoveNext();
}

pRst->Close();
pConn->Close();

// Stop using the Connection events

hr = pConn->QueryInterface(__uuidof(IConnectionPointContainer),
(void **) &pCPC);
if (FAILED(hr)) return rc;
hr = pCPC->FindConnectionPoint(__uuidof(ConnectionEvents), &pCP);
pCPC->Release();
if (FAILED(hr)) return rc;
hr = pCP->Unadvise( dwConnEvt );
pCP->Release();
if (FAILED(hr)) return rc;

// Stop using the Recordset events
hr = pRst->QueryInterface(__uuidof(IConnectionPointContainer),
(void **) &pCPC);
if (FAILED(hr)) return rc;
hr = pCPC->FindConnectionPoint(__uuidof(RecordsetEvents), &pCP);
pCPC->Release();
if (FAILED(hr)) return rc;
hr = pCP->Unadvise( dwRstEvt );
pCP->Release();
if (FAILED(hr)) return rc;

CoUninitialize();
return 1;
}

Tips of COM technique


COM 初始化:


方法一:


::CoInitialize( NULL );
... ...


::CoUninitialize();


方法二:


AfxOleInit();

Client获取COM组件相应接口的三种方式:


方法一:直接#include发布的两个头文件:


#include "ComServer.h" // 着两个文件是由idl 编译生成的,里面有interface的声明
#include "ComServer_i.c"


方法二:#import dll 或tlb:


#import "ComServer.dll" no_namespace; //编译后会生成ComServer.tlh 和 ComServer.tli文件
#import "ComServer.tlb"


方法三:直接copy ComServer.tlh和ComServer.tli到project中,然后include .tlh文件


#include "ComServer.tlh"


idl,tlb,tlh,tli


.idl => .h, _i.c, .tlb
.tlb =>.tlh, .tli


普通COM接口调用方法:


方法一:


::CoInitialize( NULL );
IUnknown * pUnk = NULL;
IFun * pFun = NULL;
hr = ::CoCreateInstance(
CLSID_Fun,
NULL,
CLSCTX_INPROC_SERVER, // 以进程内组件 DLL 方式加载
IID_IUnknown, // 想要取得 IUnknown 接口指针
(LPVOID *) &pUnk);

hr = pUnk->QueryInterface( // 从 IUnknown 得到其它接口指针
IID_IFun, // 想要取得 IFun 接口指针
(LPVOID *)&pFun );
pFun->Do( );


if( pUnk ) pUnk->Release();
if( pFun ) pFun->Release();


::CoUninitialize();


方法二:


CComPtr < IUnknown > spUnk; // 定义 IUnknown 智能指针
CComPtr < IFun > spFun; // 定义 IFun 智能指针
//用 CLSID 启动组件
hr = spUnk.CoCreateInstance( CLSID_Fun );
hr = spUnk.QueryInterface( &spFun );
spFun->Do( );


或不通过IUnknow,直接create:


hr = spFun .CoCreateInstance( CLSID_Fun );
spFun->Do( );

方法三:


CComPtr < IUnknown > spUnk; // 智能指针 IUnknown
CComQIPtr < IFun > spFun; // 智能指针 IFun
// 使用 ProgID 启动组件
hr = spUnk.CoCreateInstance( L"Simple2.fun.1" );
spFun = spUnk; // CComQIPtr 会帮我们自动调用 QueryInterface
spFun->Do( );


方法四:


// 不再经过 IUnknown
CComQIPtr < IFun, &IID_IFun > spFun; // 定义 IFun 智能指针
hr = spFun.CoCreateInstance( L"Simple2.fun.1" );
spFun->Do( );


方法五:


//#import 的方式,编译后产生 .tlh 和 .tlh 的智能指针包装,其包装形式是:IxxxPtr,xxx 表示接口名。
IFunPtr spFun; //智能指针包装
HRESULT hr = spFun.CreateInstance( L"Simple2.fun.1" ); // 使用 ProgID
// HRESULT hr = spFun.CreateInstance( __uuidof( Fun ) ); // 使用 CLSID
spFun->Do( );


方法六:


// 同方法五,但这次使用智能指针的构造函数启动组件,书写简单。
// 但也有缺点,因为如果失败的话,不知道错误原因
//IFunPtr spFun( L"Simple2.fun.1" ); // ProgID 方式
IFunPtr spFun( __uuidof(Fun) ); // CLSID 方式
spFun->Do( );


IDispatch接口调用方法:


方法一:原始的调用方法


void Func{


::CoInitialize( NULL ); // COM 初始化


CLSID clsid; // 通过 ProgID 得到 CLSID
HRESULT hr = ::CLSIDFromProgID( L"Simple8.DispSimple.1", &clsid );
ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明没有注册组件


IDispatch * pDisp = NULL; // 由 CLSID 启动组件,并得到 IDispatch 指针
hr = ::CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IDispatch, (LPVOID *)&pDisp );
ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明没有初始化 COM


LPOLESTR pwFunName = L"Add"; // 准备取得 Add 函数的序号 DispID
DISPID dispID; // 取得的序号,准备保存到这里
hr = pDisp->GetIDsOfNames( // 根据函数名,取得序号的函数
IID_NULL,
&pwFunName, // 函数名称的数组
1, // 函数名称数组中的元素个数
LOCALE_SYSTEM_DEFAULT, // 使用系统默认的语言环境
&dispID ); // 返回值
ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明组件根本就没有 ADD 函数


VARIANTARG v[2]; // 调用 Add(1,2) 函数所需要的参数
v[0].vt = VT_I4; v[0].lVal = 2; // 第二个参数,整数2
v[1].vt = VT_I4; v[1].lVal = 1; // 第一个参数,整数1


DISPPARAMS dispParams = { v, NULL, 2, 0 }; // 把参数包装在这个结构中
VARIANT vResult; // 函数返回的计算结果


hr = pDisp->Invoke( // 调用函数
dispID, // 函数由 dispID 指定
IID_NULL,
LOCALE_SYSTEM_DEFAULT, // 使用系统默认的语言环境
DISPATCH_METHOD, // 调用的是方法,不是属性
&dispParams, // 参数
&vResult, // 返回值
NULL, // 不考虑异常处理
NULL); // 不考虑错误处理
ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明参数传递错误


CString str; // 显示一下结果
str.Format("1 + 2 = %d", vResult.lVal );
AfxMessageBox( str );


pDisp->Release(); // 释放接口指针
::CoUninitialize(); // 释放 COM


}


方法二:CComDispatchDriver


void CUse2Dlg::OnBnClickedOk()
{
// 在 App 类,InitInstance 中,已经调用 AfxOleInit() 进行了 COM 初始化


CLSID clsid; // 通过 ProgID 取得组件的 CLSID
HRESULT hr = ::CLSIDFromProgID( L"Simple8.DispSimple.1", &clsid );
ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明没有注册组件


CComPtr < IUnknown > spUnk; // 由 CLSID 启动组件,并取得 IUnknown 指针
hr = ::CoCreateInstance( clsid, NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID *)&spUnk );
ASSERT( SUCCEEDED( hr ) );


CComDispatchDriver spDisp( spUnk ); // 构造只能指针
CComVariant v1(1), v2(2), vResult; // 参数
hr = spDisp.Invoke2( // 调用2个参数的函数
L"Add", // 函数名是 Add
&v1, // 第一个参数,值为整数1
&v2, // 第二个参数,值为整数2
&vResult); // 返回值
ASSERT( SUCCEEDED( hr ) ); // 如果失败,说明或者没有 ADD 函数,或者参数错误


CString str; // 显示一下结果
str.Format("1 + 2 = %d", vResult.lVal );
AfxMessageBox( str );


// spUnk 和 spDisp 都是智能指针,会自动释放
// 如果使用 CoInitialize(NULL) 初始化,则必须在 CoUninitialize() 之前
// 调用 spUnk.Release() 和 spDisp.Release() 释放
}


方法三:MFC Class from TypeLib,但它本质上是使用 IDispatch 接口,所以执行效率稍差


// 使用添加"类型库中的 MFC 类"的方式调用自动化接口
// 选择组件后,会产生相应的包装类,于是下面就可以使用了
#include "CDispSimple.h"


void CUse3Dlg::OnBnClickedOk()
{
// COM 初始化,在 App 的InitInstance() 中调用了 AfxOleInit()


CDispSimple spDisp;
if( !spDisp.CreateDispatch( _T("Simple8.DispSimple.1") ) ) // 启动组件
{
AfxMessageBox( _T("启动失败。组件注册了吗?COM 初始化了吗?") );
return ;
}
CString str = spDisp.Upper( _T("hello") ); // 调用转换为大写的函数
AfxMessageBox( str );


spDisp.ReleaseDispatch();
}


方法四:使用 #import 方式使用自动化接口,对双接口组件,直接调用自定义接口函数,不再经过 IDispatch,因此执行效率最高。


#import "..\Simple8.dll" no_namespace
void CUse4Dlg::OnBnClickedOk()
{
// COM 初始化在 App 的 InitInstance() 函数中调用 AfxOleInit()


try
{
// 通过构造函数启动组件
IDispSimplePtr spDisp( _T("Simple8.DispSimple.1") );


// 调用转化为大写的函数
_bstr_t str = spDisp->Upper( _T("hello") );


// 显示一下结果
AfxMessageBox( str );
}
catch(_com_error &e)
{
AfxMessageBox( e.ErrorMessage() );
}
}


智能指针的释放:


::CoInitialize( NULL ); // 如果在这里进行 COM 初始化,要注意智能指针的释放
CComQIPtr < IFun, &IID_IFun > spFun;
hr = spFun.CoCreateInstance( CLSID_Fun );
spFun->Do( );
// spFun->Release(); // 大错特错!!!
spFun.Release(); // 正解
::CoUninitialize();


Compiler COM Support Classes




















_bstr_t



Wraps the BSTR type to provide useful operators and methods.



_com_error



Defines the error object thrown by _com_raise_error in most failures.



_com_ptr_t



Encapsulates COM interface pointers, and automates the required calls to AddRef, Release, and QueryInterface.



_variant_t



Wraps the VARIANT type to provide useful operators and methods.



BSTR,_bstr_t, CComBSTR(ATL), CString(MFC)


// IFun::Cat() 最后一个参数是 [out] 方向属性,因此需要调用者释放
BSTR s1 = ::SysAllocString( L"Hello" );
BSTR s2 = ::SysAllocString( L" world" );
BSTR s3 = NULL;
hr = pFun->Cat( s1, s2, &s3 ); // IFun::Cat()
if( s3 ) ::SysFreeString( s3 );


CComBSTR s1( "Hello" ); // 不再使用 API 方式操作 BSTR
CComBSTR s2( " world" ); // 使用 CComBSTR 比较简单,并且
CComBSTR s3; // 最大的好处是,不用咱们自己来释放
hr = pFun->Cat( s1, s2, &s3 );


VARIANT, _variant_t,CComVariant(ATL), COleVariant(MFC)


...



通过 ProgID 得到 CLSID


CLSID clsid;
HRESULT hr = ::CLSIDFromProgID( L"Simple8.DispSimple.1", &clsid );
__uuidof(CFun)



AddRef()的几个原则:


1、启动组件得到一个接口指针(Interface)后,不要调用AddRef()。因为系统知道你得到了一个指针,所以它已经帮你调用了AddRef()函数;
  2、通过QueryInterface()得到另一个接口指针后,不要调用AddRef()。因为......和上面的道理一样;
  3、当你把接口指针赋值给(保存到)另一个变量中的时候,请调用AddRef();
  4、当不需要再使用接口指针的时候,务必执行Release()释放;
  5、当使用智能指针的时候,可以省略指针的维护工作;



MFC Resource-only dll


How to create a resource-only dll?



  • create a new Win32 DLL (non-MFC) project and add your resources to the project


  • In project's property pages->Linker->Advanced, Specify the /NOENTRY linker option.
    /NOENTRY prevents the linker from linking a reference to _main into the DLL; this option is required to create a resource-only DLL.




How to use a resource-only dll?


The application that uses the resource-only DLL should call LoadLibrary to explicitly link to the DLL.


//load resource:

HINSTANCE m_hInstResource;
m_strResFilename = _T("DataExchangeRes.dll");
m_hInstResource = ::LoadLibrary(m_strResFilename);
if (NULL != m_hInstResource){
AfxSetResourceHandle(m_hInstResource);
}


//Free Resource:

if (NULL != m_hInstResource) {
VERIFY(FreeLibrary(m_hInstResource));
m_hInstResource = NULL;
}


//Access the resource:



FindResource


LoadResource


FormatMessage


LoadAccelerators


LoadBitmap


LoadCursor


LoadIcon


LoadMenu


LoadString




Tuesday, May 29, 2007

The @ERR pseudoregister in debugging MSVC++

If you want to know the error code, you can add int test = GetLastError(); in the lines you want to know.

 

But there is a simple way to get that:

Using @ERR,hr in your watch window, you'll get the error all the time when debugging.

 

http://www.codeproject.com/debug/pseudoregister.asp

 

Tuesday, May 15, 2007

Keywords of idioms in software design.



Factory, Creator,Builder, Clone,Registry, UnRegestry,excute, manager,context,command,handler,traits,impl,accept,engine,functor


Iterator,invoke,sink,hook,fire,event,listener,instance,destroyer,startup,cleanup,provider,chain,bind,diagnose,action,logic,layer,


Dispatcher,adapter,visit,callback,assign,init,null, default,filter,holder,macro,policy,allocate,model,pool,attach,detach,controller,


Wrapper,target,destination,host, helper,process



2007


2007:


Code quality Improvement:




  • C++ Coding standards




  • Effective C++




  • More Effective C++




  • Exceptioan C++




  • More exceptional C++




  • Exceptional C++ styles




Generic programming




  • Modern C++ design.




  • STL/Loki/Boost




Windows Internals



  • Programming Application for microsoft windows

  • Microsoft Windows Internals

  • Debugging Windows


Database



  • Joe Celko's

  • The Guru's Guide to SQL server Architecture and Internals

  • Inside SQL Server 2000.




Solutions that could replace if\else and Switch\Case statements.


Traits


policy


strategy


command


State


visitor


Iterpretor


读书计划


5,6,7


C++ Coding standards


Effective C++


More Effective C++


Exceptional C++


Exceptional C++ Styles


Windows Internals


Windows核心编程


Joe Celko's SQL for Smarties: Advanced SQL Programming


Joe Celko's Trees and Hierarchies in SQL for Smarties

Sunday, April 22, 2007

Boost investigation shedule:

Boost Investigation schedule:

 

Pool

Any, Array,Tuple,

Bind,

Function, Hash,

SmartPtr,

Type Traits, Type Lists and MPL,

Serialization,

Regex,

Utility: date_time,filesystem,

Signals,

Spirit

Thread.

Wednesday, April 4, 2007

Generic programming: Overloading the << Operator for Your Own Classes

Output streams use the insertion (<<) operator for standard types. You can also overload the << operator for your own classes.

Collapse imageExample

The write function example showed the use of a Date structure. A date is an ideal candidate for a C++ class in which the data members (month, day, and year) are hidden from view. An output stream is the logical destination for displaying such a structure. This code displays a date using the cout object:

 

CopyCode imageCopy Code

Date dt( 1, 2, 92 );

cout << dt;

To get cout to accept a Date object after the insertion operator, overload the insertion operator to recognize an ostream object on the left and a Date on the right. The overloaded << operator function must then be declared as a friend of class Date so it can access the private data within a Date object.

 

CopyCode imageCopy Code

// overload_date.cpp

// compile with: /EHsc

#include <iostream>

using namespace std;

 

class Date

{

    int mo, da, yr;

public:

    Date(int m, int d, int y)

    {

        mo = m; da = d; yr = y;

    }

    friend ostream& operator<<(ostream& os, const Date& dt);

};

 

ostream& operator<<(ostream& os, const Date& dt)

{

    os << dt.mo << '/' << dt.da << '/' << dt.yr;

    return os;

}

 

int main()

{

    Date dt(5, 6, 92);

    cout << dt;

}

Output

 

5/6/92

Collapse imageRemarks

The overloaded operator returns a reference to the original ostream object, which means you can combine insertions:

 

CopyCode imageCopy Code

cout << "The date is" << dt << flush;

 

Exception hierachy in C++

Exception in STL:


Exception in MFC:




C++ Generic Programming: Sample of traits.


#include <windows.h>
#include <string>


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


// Use of templates to call WIN32 API functions.


namespace win32 {


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


template< class _Ch >
struct module_traits;


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


class module {
typedef HMODULE _Myhandle;
public:
typedef _Myhandle handle_type;
typedef DWORD size_type;
static const size_type max_length = MAX_PATH;
module() :
m_handle(GetCurrentModule())
{ }
// No need to close module handles.
// TODO: check this assumption
~module()
{ }
// Using ::GetModuleFileName, obtain the full path name of m_handle.
template< class _Ch >
std::basic_string< _Ch >& get_filename(std::basic_string< _Ch >& _fn)
{
typedef module_traits< _Ch > _Tr;
_fn.resize(max_length);
size_type _n = _Tr::GetFileName(m_handle, &*_fn.begin(), max_length);
_fn.resize(_n);
return _fn;
}
// Using ::GetModuleFileName, obtain the full path name of m_handle.
template< class _Ch, class _AfxTr >
ATL::CStringT< _Ch, _AfxTr >&
get_filename(ATL::CStringT< _Ch, _AfxTr >& _fn)
{
typedef module_traits< _Ch > _Tr;
size_type _n = _Tr::GetFileName(m_handle, _fn.GetBuffer(max_length), max_length);
_fn.ReleaseBufferSetLength(_n);
return _fn;
}
// Using ::GetModuleFileName, obtain the full path name of m_handle.
template< class _Ch, size_t _Len >
size_type get_filename(_Ch (&_buff)[ _Len ])
{
typedef module_traits< _Ch > _Tr;
return _Tr::GetFileName(m_handle, _buff, _Len);
}
// Using ::GetModuleFileName, obtain the full path name of m_handle.
template< class _Ch >
size_type get_filename(_Ch* _buff, size_t _len)
{
typedef module_traits< _Ch > _Tr;
return _Tr::GetFileName(m_handle, _buff, _len);
}
private:
// TODO: implement this.
_Myhandle GetCurrentModule()
{ return NULL; }
_Myhandle m_handle;
};


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


template< >
struct module_traits< char > {
static char space() { return ' '; }
static DWORD GetFileName(HMODULE _h, char* _buff, DWORD _size)
{ return ::GetModuleFileNameA(_h, _buff, _size); }
};


template< >
struct module_traits< wchar_t > {
static char space() { return L' '; }
static DWORD GetFileName(HMODULE _h, wchar_t* _buff, DWORD _size)
{ return ::GetModuleFileNameW(_h, _buff, _size); }
};


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


}; // namespace win32



Client 1:




try {
// Get our module path and file name and save as m_self.
win32::module _self;
_self.get_filename(m_self);



// Locate our "home" directory and any other interesting places
// and tuck them away for later reference via GetApp().
int _slash = m_self.ReverseFind(_T('\\'));
if (_slash >= 0) {
m_bin = m_self.Left(_slash);
m_home = FindHome(m_bin);
} else {
m_bin = _T(".");
m_home = m_bin;
}




// This is our resource path.
m_res = m_home + _T("\\Resource");

// This is our help path.
m_help = m_home + _T("\\Help");
// Welcome dialog is entire application
//CWelcomeDlg welcomeDlg;
//m_pMainWnd = &welcomeDlg;
//welcomeDlg.DoModal();
m_pMainWnd = &m_WelcomeDlg;
m_WelcomeDlg.DoModal();




// Must not let MFC try to close this window!
m_pMainWnd = NULL;
}




catch (const std::exception& _ex) {
CString _what(_ex.what());
AfxMessageBox(_what);
}


Client 2:


// Provides standard stream output for CString.
template< class _Ch, class _Tr, class _AfxTr > inline
std::basic_ostream< _Ch, _Tr >&
operator<<(std::basic_ostream< _Ch, _Tr >& _out,
const ATL::CStringT< _Ch, _AfxTr >& _str)
{ return _out << static_cast<const _Ch*>(_str); }


int main(int _argc, char** _argv)
{
using namespace std;

win32::module _module;


std::string _filename;
_module.get_filename(_filename);
cout << "I am module: " << _filename << " -- hello, world!" << endl;


std::wstring _wfilename;
_module.get_filename(_wfilename);
wcout << L"I am module: " << _wfilename << L" -- hello, world!" << endl;


CStringA _cfilename;
_module.get_filename(_cfilename);
cout << "I am module: " << _cfilename << " -- hello, world!" << endl;


CStringW _cwfilename;
_module.get_filename(_cwfilename);
wcout << L"I am module: " << _cwfilename << L" -- hello, world!" << endl;


char _afilename[ 300 ];
_module.get_filename(_afilename);
cout << "I am module: " << _afilename << " -- hello, world!" << endl;


win32::GetModuleFileName(NULL, _afilename);
cout << "I am module: " << _afilename << " -- hello, world!" << endl;


return 0;
}


C++ Generic Programming: Traits: The else-if-then of Types


http://www.jjhou.com/myan-type-traits.htm


麽是traits,为什麽人们把它认为是 C++ Generic Programming 的重要技术?
简短截说,traits如此重要,是因为此项技术允许系统在编译时根据类型作一些决断,
就好像在运行时根据值来作出决断一样。更进一步,此技术遵循"另增一个间接层"
的谚语,解决了不少软件工程问题,traits使您能根据其产生的背景(context)
来作出抉择。这样最终的代码就变得清晰易读,容易维护。如果你正确运用了traits
技术,你就能在不付出任何性能和安全代价的同时得到这些好处,或者能够契合其他
解决方案上的需求。
例子:Traits不仅是泛型程序设计的核心工具,而且我希望以下的例子能够使你相信,
在非常特定的问题中,它也是很有用的。
假设你现在正在编写一个关系数据库应用程序。可能您一开始用数据库供应商提供的
API库来进行反问数据库的操作。但是理所当然的,不久之後你会感到不得不写一些
包装函数来组织那些原始的API,一方面是为了简洁,另一方面也可以更好地适应
你手上的任务。这就是生活的乐趣所在,不是吗?
一个典型的API是这样的:提供一个基本的方法用来把游标(cursor, 一个行集和或
者查询结果)处的原始数据传送到内存中。现在我们来写一个高级的函数,用来把某
一列的值取出来,同时避免暴露底层的细节。这个函数可能会是这个样子:
(假想的DB API用db或DB开头)
// Example 1: Wrapping a raw cursor int fetch
// operation.
// Fetch an integer from the
// cursor "cr"
// at column "col"
// in the value "val"
void FetchIntField(db_cursor& cr,
unsigned int col, int& val)
{
// Verify type match
if (cr.column_type[col] != DB_INTEGER)
throw std::runtime_error(
"Column type mismatch");
// Do the fetch
db_integer temp;
if (!db_access_column(&cr, col))
throw std::runtime_error(
"Cannot transfer data");
memcpy(&temp, cr.column_data[col],
sizeof(temp));
// Required by the DB API for cleanup
db_release_column(&cr, col);
// Convert from the database native type to int
val = static_cast<int>(temp);
}
这种接口函数我们所有人都可能不得不在某个时候写上一遍,它不好对付但又非常重
要,处理了大量细节,而且这还只是一个简单的例子。FetchIntField抽象,提供了
高一层次的功能,它能够从游标处取得一个整数,不必再担心那些纷繁的细节。
既然这个函数如此有用,我们当然希望尽可能重用它。但是怎麽做?一个很重要的泛化
步骤就是让这个函数能够处理int之外的类型。为了做到这一点,我们得仔细考虑代码中
跟int类型相关的部份。但首先,DB_INTEGER和db_integer是什麽意思,它们是打哪儿
来的?是这样,关系数据库供应商通常随API提供一些type-mapping helpers,为其所
支持的每种类型和简单的结构定义一个符号常量或者typedef,把数据库类型对应到
C/C++类型上。
下面是一段假想的数据库API头文件:
#define DB_INTEGER 1
#define DB_STRING 2
#define DB_CURRENCY 3
...
typedef long int db_integer;
typedef char db_string[255];
typedef struct {
int integral_part;
unsigned char fractionary_part;
} db_currency;
...
我们试 来写一个FetchDoubleField函数,作为走向泛型化的第一步。此函数从游标处得到
一个double值。数据库本身提供的类型映像(type mapping)是db_currency,但是我们希望
能用double的形式来操作。FetchDoubleField看上去跟FetchIntField很相似,简直就是孪
生兄弟。例2:
// Example 2: Wrapping a raw cursor double fetch operation.
//
void FetchDoubleField(db_cursor& cr, unsigned int col, double& val)
{
if (cr.column_type[col] != DB_CURRENCY)
throw std::runtime_error("Column type mismatch");
if (!db_access_column(&cr, col))
throw std::runtime_error("Cannot transfer data");
db_currency temp;
memcpy(&temp, cr.column_data[col], sizeof(temp));
db_release_column(&cr, col);
val = temp.integral_part + temp.fractionary_part / 100.;
}
看上去很像FetchIntField吧 我们可不想对每一个类型都写一个单独的函数,所以
如果能够在一个地方把FetchIntField, FetchDoubleField以及其他的Fetch函数合
为一体就好了。
我们把这两片代码的不同之处列举如下:
·输入类型:double/int
·内部类型:db_currency/db_integer
·常数值类型:DB_CURRENCY/DB_INTEGER
·算法:一个表达式/static_cast
输入类型(int/double)与其他几点之间的对应关系看上去没什麽规律可循,而是很随意,
跟数据库供应商(恰好)提供的类型关系密切。Template机制本身无能为力,它没有提供
如此先进的类型推理机制。也没法把不同的类型用继承关系组织起来,因为我们处理的是
原始类型。受到API的限制以及问题本身的底层特性,乍看上去我们好像没辙了。不过我们
还有一条活路。
进入TRAITS大门:Traits技术就是用来解决上述问题的:把与各种类型相关的代码片断合体,
并且具有类似and/or结构的能力,到时可以根据不同的类型产生不同的变体。
Traits依赖显式模版特殊化(explicit template specialization)机制来获得这种结果。
这一特性使你可以为每一个特定的类型提供模板类的一个单独实现,见例3:
// Example 3: A traits example
//
template <class T>
class SomeTemplate
{
// generic implementation (1)
...
};
// 注意下面特异的语法
template <>
class SomeTemplate<char>
{
// implementation tuned for char (2)
...
};
...
SomeTemplate<int> a; // will use (1)
SomeTemplate<char*> b; // will use (1)
SomeTemplate<char> c; // will use (2)
如果你用char类型来实例化SomeTemplate类模板,编译器会用那个显式的模板声明来特殊化。
至於其他的类型,当然就用那个通用模板来实例化。这就像一个由类型驱动if-statement。
通常最通用的模板(相当于else部份)最先定义,if-statement靠後一点。你甚至可以决定
完全不提供通用的模板,这样只有特定的实例化是允许的,其他的都会导致编译错误。
现在我们把这个语言特性跟手上的问题联系起来。我们要实现一个模板函数FetchField,
用需要读取的类型作为叁数来实例化。在该函数内部,我会用一个叫做TypeId的东西代表
那个符号常量,当要获取int型值时它的值就是DB_INTEGER,当要获取double型值时它的
值就是DB_CURRENCY。否则,就必须在编译时报错。类似的,根据要获取的类型的不同,
我们还需要操作不同的API类型(db_integer/db_currency)和不同的转换算法(表达式/static_cast).
让我们用显式模板特殊化机制来解决这个问题。我们得有一个FetchField,可以针对一个
模板类来产生不同的变体,而那个模板类又能够针对int和double进行显式特殊化。每个
特殊化都必须为这些变体提供统一的名称。
// Example 4: Defining DbTraits
//
// Most general case not implemented 最通用的情况没有实现
template <typename T> struct DbTraits;
// Specialization for int
template <>
struct DbTraits<int>
{
enum { TypeId = DB_INTEGER };
typedef db_integer DbNativeType;
// 注意下面的Convert是static member function 译者
static void Convert(DbNativeType from, int& to)
{
to = static_cast<int>(from);
}
};
// Specialization for double
template <>
struct DbTraits<double>
{
enum { TypeId = DB_CURRENCY };
typedef db_currency DbNativeType;
// 注意下面的Convert是static member function 译者
static void Convert(const DbNativeType& from, double& to)
{
to = from.integral_part + from.fractionary_part / 100.;
}
};
现在,如果你写DbTraits<int>::TypeId,你得到的就是DB_INTEGER,而对於
DbTraits<double>::TypeId,得到的就是DB_CURRENCY,对於
DbTraits<anything_else>::TypeId,得到的是什麽呢?Compile-time error!
因为模板类本身只是声明了,并没有定义。
是不是一劳永逸了?看看我们如何利用DbTraits来实现FetchField就放心了。
我们把所有变化的部份 枚举类型 API类型 转换算法 都放在了DbTraits
里,这下我们的函数里只包含FetchIntField和FetchDoubleField的相同部份了:
// Example 5: A generic, extensible FetchField using DbTraits
//
template <class T>
void FetchField(db_cursor& cr, unsigned int col, T& val)
{
// Define the traits type
typedef DbTraits<T> Traits;
if (cr.column_type[col] != Traits::TypeId)
throw std::runtime_error("Column type mismatch");
if (!db_access_column(&cr, col))
throw std::runtime_error("Cannot transfer data");
typename Traits::DbNativeType temp;
memcpy(&temp, cr.column_data[col], sizeof(temp));
Traits::Convert(temp, val);
db_release_column(&cr, col);
}
搞定了 我们只不过实现和使用了一个traits模板类而已
Traits依靠显式模板特殊化来把代码中因类型不同而发生变化的片断拖出来,用统一的
接口来包装。这个接口可以包含一个C++类所能包含的任何东西:内嵌类型,成员函数,
成员变量,作为客户的模板代码可以通过traits模板类所公开的接口来间接访问之。
这样的traits接口通常是隐式的,隐式接口不如函数签名(function signatures)那麽
严格,例如,尽管DbTraits<int>::Convert和DbTraits<double>::Convert有 非常不
同的签名,但它们都可以正常工作。
Traits模板类在各种类型上建立一个统一的接口,而又针对各种类型提供不同的实现细节。
由於Traits抓住了一个概念,一个相关联的选择集,所以能够在相似的contexts中被重用。
定义: A traits template is a template class, possibly explicitly
specialized, that provides a uniform symbolic interface over a coherent
set of design choices that vary from one type to another.

Traits模板是一个模板类,很可能是显式特殊化的模板类,它为一系列根据不同类
型做出的设计选择提供了一个统一的 符号化的接口。
TRAITS AS ADAPTERS: 用作适配子的TRAITS



Monday, April 2, 2007

项目升级的变化点

今天下午粗略的罗列的下项目升级需要做的事情。这些需要重写代码的地方就是变化点了。

是否可以用Design patten 包装下?

在最初设计的时候是否就已经预见到了这些变化点了呢?是否采取过行动了?

Four type of inline member function.

Inline 的四种写法:

 

//Case 1: 在函数体内实现其定义,虽然没有显示的声明inline关键字,但是这些函数被自动作为inline函数处理,

//被成为"在类定义中定义的内联(inline)函数".

class List

{

public:

            void PrintMe()

            {

                        cout << "List";

            }

};

 

//Case 2: 在函数体内实现其定义,并显示的声明inline关键字,当然,这个inline关键字完全是冗余的。

//被成为"在类定义中定义的内联(inline)函数".

class List

{

public:

            inline void PrintMe()

            {

                        cout << "List";

            }

};

 

//Case 3: 在类体外定义的成员函数不是inline的,

//但是如果显示的在类体内中的声明上加上inline关键字,则其也成为inline函数。

class List

{

public:

            void PrintMe();

};

 

//必须在List.h

inline void List::PrintMe()

{

            cout << "List";

}

 

//List.h

//Case 4: 在类体外定义的成员函数不是inline的,

//但是如果在类体外出现的函数定义上显示的使用inline关键字,则其也成为inline函数。

class List

{

public:

            inline void PrintMe();

};

 

//必须在List.h中,并且这里函数前可加inline,也可不加。

void List::PrintMe()

{

            cout << "List";

}

 

//Notice:由于inline函数必须在调用它的每个文本文件中被定义,

//所以没有在类体中定义的inline成员函数必须被放在类定义出现的.h文件中,并且在类定义之后。

//否则compilor会报错!