【问题标题】:MSVC std::exception doesn't comply with standard?MSVC std::exception 不符合标准?
【发布时间】:2012-02-20 19:54:07
【问题描述】:

我实现了一个异常类WINERR_EXCEPTION 用于抛出GetLastError() 的运行时描述!

它按预期工作。 然后我想消除不必要的分配。 为此,我实现了一个临时类,它接受并存储由what() 返回的const char*

但是,当我使用 interim_exception 类时,std::exception 的 MSVC(c++11 开发人员预览版)实现使用 std::exception 基类成员变量(而不是 what())来复制描述字符串。 我得到"unknown exception" 作为what() 的返回值。

标准说

exception& operator=(const exception& rhs) noexcept;
    Effects: Copies an exception object.
    Postcondition: If *this and rhs both have dynamic type exception then strcmp(what(), rhs.what()) shall equal 0.

这与后置条件中的 dynamic 一词有关(我的 interim_exception 的描述不是动态分配的)还是这只是一个错误的实现?

msvc 的 std::exception::operator= () :

_EXCEPTION_INLINE exception& __CLR_OR_THIS_CALL exception::operator=(const exception& _That)
    {
    if (this != &_That)
        {
        _Tidy(); // resets members

        if (_That._Mydofree) // NB. This prevents my intentions (evals to false)
            {
            _Copy_str(_That._Mywhat);
            }
        else
            {
            _Mywhat = _That._Mywhat;
            }
        }

    return *this;
    }

我的代码:

////////////////////////////////////////////////////////////////////////////////
// file : TstThrow.h
// purpose : implementation of class WINERR_EXCEPTION
////////////////////////////////////////////////////////////////////////////////
//

#include <windows.h> 
#include <exception>
#include <stdexcept>

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

namespace 
{
    // class interim_exception to avoid unnecessary allocations
    class interim_exception 
        : public std::exception
    {
    public: 
        // override member : what() const
    const char * what() const
    //_EXCEPTION_INLINE virtual const char * __CLR_OR_THIS_CALL what() const
    { return m_What; }

        interim_exception( const char* szWhat )
            :m_What( szWhat ? szWhat : "" ) {}

    private:
        const char* m_What;

    };
}

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

class WINERR_EXCEPTION 
    : public std::exception
{
public:
    DWORD    ErrNo () const  { return m_ErrNo; }

public:
    explicit WINERR_EXCEPTION ( DWORD dwErrNo );

    inline exception& operator= ( const exception& that )
    {
        exception::operator= ( that );
        return (*this);
    }

    inline exception& operator= ( const char* szWhat )
    { 
        operator= ( std::exception( szWhat )); /* this works OK */
        // exception::operator= ( std::exception( szWhat )); /* this works OK */
        // exception::operator= (interim_exception( szWhat )); /* this doesn't work */
        // operator= (interim_exception( szWhat )); /* this doesn't work */
        return (*this);
    }

private:
    DWORD    m_ErrNo;

};

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


WINERR_EXCEPTION::WINERR_EXCEPTION ( DWORD dwErrNo )
    :m_ErrNo (dwErrNo) //exception("") ,
{
    DWORD    dwFrmtFlags (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER);
    dwFrmtFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
    dwFrmtFlags |= FORMAT_MESSAGE_MAX_WIDTH_MASK;    // no newlines

    LPSTR    pBuffer (nullptr);
    if (! ::FormatMessageA( dwFrmtFlags ,nullptr 
                           ,dwErrNo ,0 ,(LPSTR)&pBuffer ,0 ,nullptr ))
    {
        dwErrNo = GetLastError();
        if (dwErrNo == ERROR_MR_MID_NOT_FOUND)
            operator= ( WINERR_EXCEPTION( dwErrNo ).what() );
        else
            operator= ( "Substituted Error Message :\n\t" 
                        "Could not allocate buffer for ORIGINAL Error Message\n" );
    }
    else
    {
        operator= ( pBuffer );
        LocalFree( pBuffer );
    }
}

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

我的测试:

void     TstThrow ()
{
    for ( DWORD dwErr = ERROR_SUCCESS; dwErr < 200; ++dwErr )
    {
        SetLastError( dwErr );
        try
      {
          throw ::WINERR_EXCEPTION( GetLastError() );
      }
      catch (const ::WINERR_EXCEPTION& werr)
      {
          ::WINERR_EXCEPTION err ( werr ); // test for copying of object !

          std::cout << std::setw(4) << werr.ErrNo() << " :"<< werr.what() << std::endl;
        }

      if ((dwErr % 100) == 0)
          Sleep(1500);
    }
}

【问题讨论】:

  • 我很困惑。 ::WINERR_EXCEPTION 定义在哪里?
  • 您应该将szWhat 转发给std::exception 的构造函数,采用char const*,而不是自己将其存储为数据成员。见msdn.microsoft.com/en-us/library/c4ts6d5a.aspx
  • @Rob : 在包含文件“tstthrow.h”中(我已提供)。
  • @Rob:在第二个代码块中;您必须滚动到第一个类定义才能找到它。
  • 顺便说一句,你不应该在头文件中放置一个未命名的命名空间——这很容易导致违反单一定义规则。

标签: c++ exception standards-compliance


【解决方案1】:

如果两个对象的动态类型都是exception,而不是异常的子类型,则这两个对象“都具有动态类型exception”。所以后置条件不适用于您的情况,因为动态类型是 WINERR_EXCEPTION,而不是 exception

如果您希望what() 返回的不是std::exception() 提供的默认值,那么您必须自己覆盖它,或者从覆盖它的东西继承,例如@987654327 中定义的异常类型@。 (至少在标准 C++ 中是这样的;我不知道微软对std::exception 的扩展应该如何工作,所以我无法评论它们)。

【讨论】:

  • 我对此有点困惑,因为动态类型有两种定义(1.3.7 和 1.3.8),我认为 1.3.8 适用于我的情况。
  • @Edwin:不,1.3.7 适用,因为引用是左值。 1.3.8 仅适用于对象本身;在这种情况下,动态和静态类型是相同的。
  • 关于您的编辑,这就是我所做的,但 MSVC 的实现没有使用 what() 它使用原始异常基类对象的成员变量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-13
  • 1970-01-01
  • 1970-01-01
  • 2015-04-17
  • 1970-01-01
  • 2019-07-11
  • 1970-01-01
相关资源
最近更新 更多