COMインタフェースをC++で実装していると、例外をcatchしてHRESULTのエラー値を返すコードを非常によく使います。毎回大雑把な実装で済ませていましたが、今回ちゃんとしたものを作ろうと思い立ちました。

std::exceptionまたはその派生クラスのオブジェクトからHRESULT値を生成する関数を書きました。その過程でstd::errc (errno)からHRESULT値を生成する関数も書きました。

#include <exception>
#include <system_error>
#include <ios> // iostream_category
#include <future> // future_category
#include <typeinfo> // bad_cast
#include <windows.h>
 
HRESULT HResultFromErrno(std::errc value)
{
  using std::errc;
  switch (value)
  {
    case errc::address_family_not_supported: return HRESULT_FROM_WIN32(WSAEAFNOSUPPORT); // default_condition
    case errc::address_in_use: return HRESULT_FROM_WIN32(WSAEADDRINUSE); // default_condition
    case errc::address_not_available: return HRESULT_FROM_WIN32(WSAEADDRNOTAVAIL); // default_condition
    case errc::already_connected: return HRESULT_FROM_WIN32(WSAEISCONN);
    case errc::argument_list_too_long: return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
    case errc::argument_out_of_domain: return E_INVALIDARG;
    case errc::bad_address: return HRESULT_FROM_WIN32(WSAEFAULT); // default_condition
    case errc::bad_file_descriptor: return HRESULT_FROM_WIN32(WSAEBADF); // default_condition
    case errc::bad_message: return HRESULT_FROM_WIN32(ERROR_CANTREAD);
    case errc::broken_pipe: return HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE);
    case errc::connection_aborted: return HRESULT_FROM_WIN32(WSAECONNABORTED); // default_condition
    case errc::connection_already_in_progress: return HRESULT_FROM_WIN32(WSAEALREADY); // default_condition
    case errc::connection_refused: return HRESULT_FROM_WIN32(WSAECONNREFUSED); // default_condition
    case errc::connection_reset: return HRESULT_FROM_WIN32(WSAECONNRESET); // default_condition
    case errc::cross_device_link: return HRESULT_FROM_WIN32(ERROR_NOT_SAME_DEVICE); // default_condition
    case errc::destination_address_required: return HRESULT_FROM_WIN32(WSAEDESTADDRREQ); // default_condition
    case errc::device_or_resource_busy: return HRESULT_FROM_WIN32(ERROR_BUSY); // default_condition
    case errc::directory_not_empty: return HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY); // default_condition
    case errc::executable_format_error: return HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
    case errc::file_exists: return HRESULT_FROM_WIN32(ERROR_FILE_EXISTS); // default_condition
    case errc::file_too_large: return HRESULT_FROM_WIN32(ERROR_FILE_TOO_LARGE);
    case errc::filename_too_long: return HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); // default_condition
    case errc::function_not_supported: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION); // default_condition
    case errc::host_unreachable: return HRESULT_FROM_WIN32(WSAEHOSTUNREACH); // default_condition
    case errc::identifier_removed: return HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE); // 意訳
    case errc::illegal_byte_sequence: return HRESULT_FROM_WIN32(ERROR_ILLEGAL_CHARACTER);
    case errc::inappropriate_io_control_operation: return HRESULT_FROM_WIN32(ERROR_INVALID_HANDLE);
    case errc::interrupted: return HRESULT_FROM_WIN32(WSAEINTR); // default_condition
    case errc::invalid_argument: return E_INVALIDARG;
    case errc::invalid_seek: return HRESULT_FROM_WIN32(ERROR_SEEK_ON_DEVICE);
    case errc::io_error: return HRESULT_FROM_WIN32(ERROR_OPEN_FAILED); // default_condition
    case errc::is_a_directory: return E_FAIL;
    case errc::message_size: return HRESULT_FROM_WIN32(WSAEMSGSIZE); // default_condition
    case errc::network_down: return HRESULT_FROM_WIN32(WSAENETDOWN); // default_condition
    case errc::network_reset: return HRESULT_FROM_WIN32(WSAENETRESET); // default_condition
    case errc::network_unreachable: return HRESULT_FROM_WIN32(WSAENETUNREACH); // default_condition
    case errc::no_buffer_space: return HRESULT_FROM_WIN32(WSAENOBUFS); // default_condition
    case errc::no_child_process: return HRESULT_FROM_WIN32(ERROR_WAIT_NO_CHILDREN);
    case errc::no_link: return E_FAIL;
    case errc::no_lock_available: return HRESULT_FROM_WIN32(ERROR_LOCK_VIOLATION); // default_condition
    case errc::no_message_available: return HRESULT_FROM_WIN32(ERROR_NO_DATA);
    case errc::no_message: return E_FAIL;
    case errc::no_protocol_option: return HRESULT_FROM_WIN32(WSAENOPROTOOPT); // default_condition
    case errc::no_space_on_device: return HRESULT_FROM_WIN32(ERROR_HANDLE_DISK_FULL); // default_condition
    case errc::no_stream_resources: return E_FAIL;
    case errc::no_such_device_or_address: return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
    case errc::no_such_device: return HRESULT_FROM_WIN32(ERROR_DEV_NOT_EXIST); // default_condition
    case errc::no_such_file_or_directory: return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); // default_condition
    case errc::no_such_process: return HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
    case errc::not_a_directory: return E_INVALIDARG;
    case errc::not_a_socket: return HRESULT_FROM_WIN32(WSAENOTSOCK); // default_condition
    case errc::not_a_stream: return E_INVALIDARG;
    case errc::not_connected: return HRESULT_FROM_WIN32(WSAENOTCONN); // default_condition
    case errc::not_enough_memory: return E_OUTOFMEMORY;
    case errc::not_supported: return CO_E_NOT_SUPPORTED;
    case errc::operation_canceled: return HRESULT_FROM_WIN32(ERROR_OPERATION_ABORTED); // default_condition
    case errc::operation_in_progress: return HRESULT_FROM_WIN32(WSAEINPROGRESS); // default_condition
    case errc::operation_not_permitted: return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
    case errc::operation_not_supported: return HRESULT_FROM_WIN32(WSAEOPNOTSUPP); // default_condition
    case errc::operation_would_block: return HRESULT_FROM_WIN32(WSAEWOULDBLOCK); // default_condition
    case errc::owner_dead: return HRESULT_FROM_WIN32(ERROR_ABANDONED_WAIT_0);
    case errc::permission_denied: return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED); // default_condition
    case errc::protocol_error: return HRESULT_FROM_WIN32(ERROR_READ_FAULT);
    case errc::protocol_not_supported: return HRESULT_FROM_WIN32(WSAEPROTONOSUPPORT); // default_condition
    case errc::read_only_file_system: return HRESULT_FROM_WIN32(ERROR_FILE_READ_ONLY);
    case errc::resource_deadlock_would_occur: return HRESULT_FROM_WIN32(ERROR_POSSIBLE_DEADLOCK);
    case errc::resource_unavailable_try_again: return HRESULT_FROM_WIN32(ERROR_RETRY); // default_condition
    case errc::result_out_of_range: return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
    case errc::state_not_recoverable: return E_FAIL;
    case errc::stream_timeout: return HRESULT_FROM_WIN32(WAIT_TIMEOUT);
    case errc::text_file_busy: return HRESULT_FROM_WIN32(ERROR_BUSY);
    case errc::timed_out: return HRESULT_FROM_WIN32(WSAETIMEDOUT); // default_condition
    case errc::too_many_files_open_in_system: return E_FAIL;
    case errc::too_many_files_open: return HRESULT_FROM_WIN32(ERROR_TOO_MANY_OPEN_FILES); // default_condition
    case errc::too_many_links: return HRESULT_FROM_WIN32(ERROR_TOO_MANY_LINKS);
    case errc::too_many_symbolic_link_levels: return E_FAIL;
    case errc::value_too_large: return DISP_E_OVERFLOW;
    case errc::wrong_protocol_type: return HRESULT_FROM_WIN32(WSAEPROTOTYPE); // default_condition
    default: return E_FAIL;
  }
}
 
HRESULT HResultFromException(const std::exception& e)
{
  if (auto se = dynamic_cast<const std::system_error*>(&e))
  {
    if (se->code().category() == std::system_category())
    {
      return HRESULT_FROM_WIN32(se->code().value());
    }
    auto ec = se->code().default_error_condition();
    if (ec.category() == std::system_category())
    {
      return HRESULT_FROM_WIN32(ec.value());
    }
    else if (ec.category() == std::generic_category())
    {
      return HResultFromErrno(static_cast<std::errc>(ec.value()));
    }
    else if (ec.category() == std::iostream_category())
    {
      return HResultFromErrno(std::errc::io_error);
    }
    else if (ec.category() == std::future_category())
    {
      return E_NOT_VALID_STATE;
    }
  }
  else if (dynamic_cast<const std::bad_alloc*>(&e))
  {
    return E_OUTOFMEMORY;
  }
  else if (dynamic_cast<const std::bad_cast*>(&e))
  {
    return HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH);
  }
  else if (dynamic_cast<const std::domain_error*>(&e))
  {
    return HResultFromErrno(std::errc::argument_out_of_domain);
  }
  else if (dynamic_cast<const std::invalid_argument*>(&e))
  {
    return HResultFromErrno(std::errc::invalid_argument);
  }
  else if (dynamic_cast<const std::length_error*>(&e))
  {
    return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
  }
  else if (dynamic_cast<const std::out_of_range*>(&e))
  {
    return E_BOUNDS;
  }
  else if (dynamic_cast<const std::overflow_error*>(&e))
  {
    return HResultFromErrno(std::errc::value_too_large); // EOVERFLOW
  }
  // logic_error, runtime_error, underflow_error,
  return E_FAIL;
}

まずsystem_errorについての処理を行っています。

code()またはdefault_condition()を通した後のerror_condition値について、error_categoryがsystem_categoryなら、Win32エラー値として扱い、HRESULT_FROM_WIN32で変換しています。

default_conditionを通した後の値に対してもsystem_categoryの判定を行っている理由はは、過去に自分が作ったerror_categoryでそのような実装を行ったことがあるためです。すなわち、default_conditionで「generic_categoryに対応するものがあればそれ、そこになくてsystem_category(Win32エラー値)に対応するものがあればそれを返す」というような実装です。

次にerror_condition値がsystem_categoryの場合の処理です。std::errc値からHRESULTに変換して返します。ここは関数HResultFromErrnoに分けました。この値の対応付けは、次のようにして選びました。

  1. 標準的なHRESULT値で適するものがある場合、その値。E_OUTOFMEMORY, E_INVALIDARGなどが該当する。
  2. Visual C++とBoostのsystem_categoryのdefault_conditionにWin32エラー値からstd::errc値への変換が存在する場合、逆変換となるようその値を選んだ。ただし、複数Win32エラー値から1つのerrc定数に対応している場合(例:ERROR_ALREADY_EXISTSとERROR_FILE_EXISTSはともにerrc::file_existsになる)、errno値の意味から最も適当と思われるものを選んだ。上記コード中、default_conditionとコメントしてあるのがこれらに該当するものである。
  3. それに該当しない場合、適する値があればそれを選んだ。
  4. 適する値がなければE_FAILとした。

その他のcategoryや型の場合も、概ね同様です。std::errc値またはHRESULT値に適当なものがあればそれを選び、どうしても適するものがなければE_FAILを返すことにしています。

errno (std::errc)を表現するためのFACILITYが設けられるなど、std::errc値をそのままHRESULTに格納できるようになっていれば、こんなに考える必要がなくて分かりやすいのになあと思いました。


スポンサード リンク

この記事のカテゴリ

  • ⇒ std::exceptionおよびerrnoからHRESULTに変換する
  • ⇒ std::exceptionおよびerrnoからHRESULTに変換する
  • ⇒ std::exceptionおよびerrnoからHRESULTに変換する