Sort function of STL Gives Unexpected Output when Vector is a Type Derived from _com_ptr_t


 


C++ code which contains an STL vector containing types derived  from  the_com_ptr_t  COM smart pointer class and using the std:sort() function to sort interface pointers may give unexpected sort output._com_ptr_t COM smart pointer derived types are commonly created when using the ‘#import’ keyword in C++ to import a COM type library. For example:


#import “msxml4.dll”


The _com_ptr_t class implementation overrides the reference (&) operator and returns a null interface pointer. This is in conflict with STL reference operator rules and may result in incorrect sorting order when std::sort () is called. 


 


Sample Code:


 


#include “stdafx.h”


#include <iostream>


#include <vector>


#include <algorithm>


#include <sstream>


 


// Create smart pointer wrappers for msxml com object


#import “msxml4.dll”


 


// Helper Function to create XML


_bstr_t CreateXML()


{


      int nums[] = {1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,4,5,6,7,8,9};


      std::stringstream tempstr;


      tempstr << “<NodesCol>” << std::endl;


      for(int i=0; i<34 ;i++)


            tempstr << “\t<Node><NodeNum>” << nums[i] << “</NodeNum></Node>” << std::endl;


      tempstr << “</NodesCol>” << std::endl;


      return static_cast<_bstr_t>(tempstr.str().c_str());


}


 


// Compare Function for std::swap


bool myCompare(MSXML2::IXMLDOMNodePtr& node1, MSXML2::IXMLDOMNodePtr& node2)


{


      int nNode1No(-1),nNode2No(-1);


      if( ( NULL == node1.GetInterfacePtr() ) || ( NULL == node2.GetInterfacePtr() ) )


            return false;


 


      _stscanf_s( node1->text, _T(“%d”), &nNode1No );


      _stscanf_s( node2->text, _T(“%d”), &nNode2No );


 


      if( nNode1No < nNode2No )


            return true;


      return false;


};


 


int _tmain(int argc, _TCHAR* argv[])


{


      // A vector of IXMLDOMNode smart pointers


      std::vector<MSXML2::IXMLDOMNodePtr> vecNodesList;


 


      CoInitialize( NULL );


 


      // Create and Load the XML document


      MSXML2::IXMLDOMDocumentPtr docPtr;


      docPtr.CreateInstance(__uuidof(MSXML2::DOMDocument40));    


      docPtr->loadXML(CreateXML());


      MSXML2::IXMLDOMNodeListPtr nodes = docPtr->selectNodes( _T(“NodesCol/Node/NodeNum”) );


 


      std::cout << “Before Sorting Nodes” << std::endl;


      // Initialize the vector of smart pointers


      for( int i = 0; i < nodes->length; i++ )


      {


            int nNodeNo = -1;


            vecNodesList.push_back( nodes->item[i] );


            std::cout << nodes->item[i]->text << std::endl;


      }


 


      // Sort the vector of smart pointers


      std::sort( vecNodesList.begin(), vecNodesList.end(), myCompare );


 


      // Display sorted results


      std::cout << “\nAfter Sorting Nodes\n” << std::endl;


      std::vector<MSXML2::IXMLDOMNodePtr>::iterator iter;


      for(iter = vecNodesList.begin(); iter!=vecNodesList.end();iter++)


      {


            if(NULL != (*iter).GetInterfacePtr())


                  std::cout << (*iter)->text << std::endl;


            else


                  std::cout << “-1” << std::endl;


            *iter = NULL; // Release smart pointer before destroying vector;


      }


 


      // Release smart pointers before CoUninitialize


      nodes = NULL;


      docPtr = NULL;


      CoUninitialize( );


      return 0;


}


 


 


 


The sort() method uses the utility function std::swap(): 


template<class _Ty> inline   


void swap(_Ty& _Left, _Ty& _Right) 



 // exchange values stored at _Left and _Right   


    if (&_Left != &_Right) 


     {  


        // different, worth swapping 


       _Ty _Tmp = _Left;


       _Left = _Right;


       _Right = _Tmp;


     }


}


 


The “if (&_Left != &_Right)” line of code calls the overloaded operator & from _com_ptr_t comip.h header file, which returns a NULL interface pointer:


 


Interface** operator&() throw()  


{


_Release();


m_pInterface = NULL;


return &m_pInterface;  


}


 



This is a known issue.


All STL containers, including vector, forbid their elements from overloading operator&().  (This is C++ Specification 03 23.1/3 “The type of objects stored in these components must meet the requirements of CopyConstructible types (20.1.3) and the additional requirements of Assignable types”, and 20.1.3/1 requires &t to have type T * and denote the address of t.)


The best solution is to never overload operator&() when using STL containers. If that is not possible (e.g. because the operator overload is coming from code that you don’t control), an alternative is to wrap the class in another wrapper class that doesn’t overload operator&() which you can then add to the vector or other container class.  CAdapt is one example of such a class. 


 


Fixed Sample Code example using CAdapt


 


#include “stdafx.h”


#include <iostream>


#include <vector>


#include <algorithm>


#include <sstream>


#include “atlcomcli.h”


 


// Create smart pointer wrappers for msxml com object


#import “msxml4.dll”


 


// Helper Function to create XML


_bstr_t CreateXML()


{


      int nums[] = {1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,4,5,6,7,8,9};


      std::stringstream tempstr;


      tempstr << “<NodesCol>” << std::endl;


      for(int i=0; i<34 ;i++)


            tempstr << “\t<Node><NodeNum>” << nums[i] << “</NodeNum></Node>” << std::endl;


      tempstr << “</NodesCol>” << std::endl;


      return static_cast<_bstr_t>(tempstr.str().c_str());


}


 


// Compare Function for std::swap


bool myCompare(MSXML2::IXMLDOMNodePtr& node1, MSXML2::IXMLDOMNodePtr& node2)


{


      int nNode1No(-1),nNode2No(-1);


      if( ( NULL == node1.GetInterfacePtr() ) || ( NULL == node2.GetInterfacePtr() ) )


            return false;


 


      _stscanf_s( node1->text, _T(“%d”), &nNode1No );


      _stscanf_s( node2->text, _T(“%d”), &nNode2No );


 


      if( nNode1No < nNode2No )


            return true;


      return false;


};


 


int _tmain(int argc, _TCHAR* argv[])


{


      // A vector of IXMLDOMNode smart pointers


      std::vector<CAdapt<MSXML2::IXMLDOMNodePtr>> vecNodesList;


 


      CoInitialize( NULL );


 


      // Create and Load the XML document


      MSXML2::IXMLDOMDocumentPtr docPtr;


      docPtr.CreateInstance(__uuidof(MSXML2::DOMDocument40));    


      docPtr->loadXML(CreateXML());


      MSXML2::IXMLDOMNodeListPtr nodes = docPtr->selectNodes(       _T(“NodesCol/Node/NodeNum”) );


 


      std::cout << “Before Sorting Nodes” << std::endl;


      // Initialize the vector of smart pointers


      for( int i = 0; i < nodes->length; i++ )


      {


            int nNodeNo = -1;


            vecNodesList.push_back( nodes->item[i] );


            std::cout << nodes->item[i]->text << std::endl;


      }


 


      // Sort the vector of smart pointers


      std::sort( vecNodesList.begin(), vecNodesList.end(), myCompare );


 


      // Display sorted results


      std::cout << “\nAfter Sorting Nodes\n” << std::endl;


      std::vector<CAdapt<MSXML2::IXMLDOMNodePtr>>::iterator iter;


      for(iter = vecNodesList.begin(); iter!=vecNodesList.end();iter++)


      {


              if(NULL != (*iter).m_T.GetInterfacePtr())


                        std::cout << (*iter).m_T->text << std::endl;


            else


                  std::cout << “-1” << std::endl;


            *iter = NULL; // Release smart pointer before destroying vector;


      }


 


      // Release smart pointers before CoUninitialize


      nodes = NULL;


      docPtr = NULL;


      CoUninitialize( );


      return 0;


}


 


 


Jyoti Patel


Developer Support VC++ and C#

Comments (2)

  1. R.J says:

    I just found your blog and I wonder how informative it is.

    I believe most of VC++ developers even don’t know this blog exists, otherwise there would be lots of comments and questions here.

    Thanks a lot,

  2. Jyoti Patel says:

    Thanks for your comment!!