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

Article ID: 967759 – Last Review: February 10, 2009 – Revision: 1.0

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

Source: Microsoft Support

RAPID PUBLISHING

RAPID PUBLISHING ARTICLES PROVIDE INFORMATION DIRECTLY FROM WITHIN THE MICROSOFT SUPPORT ORGANIZATION. THE INFORMATION CONTAINED HEREIN IS CREATED IN RESPONSE TO EMERGING OR UNIQUE TOPICS, OR IS INTENDED SUPPLEMENT OTHER KNOWLEDGE BASE INFORMATION.

Action

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

 

 

Cause

Cause:

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;  

}

Resolution

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;

}

DISCLAIMER

MICROSOFT AND/OR ITS SUPPLIERS MAKE NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY, RELIABILITY OR ACCURACY OF THE INFORMATION CONTAINED IN THE DOCUMENTS AND RELATED GRAPHICS PUBLISHED ON THIS WEBSITE (THE “MATERIALS”) FOR ANY PURPOSE. THE MATERIALS MAY INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS AND MAY BE REVISED AT ANY TIME WITHOUT NOTICE.

TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, MICROSOFT AND/OR ITS SUPPLIERS DISCLAIM AND EXCLUDE ALL REPRESENTATIONS, WARRANTIES, AND CONDITIONS WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO REPRESENTATIONS, WARRANTIES, OR CONDITIONS OF TITLE, NON INFRINGEMENT, SATISFACTORY CONDITION OR QUALITY, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, WITH RESPECT TO THE MATERIALS.


APPLIES TO
  • Microsoft Visual C++ 2008 Express Edition
Keywords: 
kbnomt kbrapidpub KB967759

 

Microsoft Knowledge Base Article

This article contents is Microsoft Copyrighted material.
Microsoft Corporation. All rights reserved. Terms of Use | Trademarks


You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

AddThis Social Bookmark Button

Leave a Reply

*
To prove that you're not a bot, enter this code
Anti-Spam Image