Skip to content

random_device: mingw gcc向けのworkaroundを具体的に書く #458

@yumetodo

Description

@yumetodo

#451 の議論の結果random_deviceが擬似乱数生成器で定義される可能性があることに触れたが、その最も有名な[要出典]実装であるmingw gcc向けのwork aroundを紹介してもいいのではないか。

イメージとしてはこんな感じのを載せる。rand_sWindows.hの前に_CRT_RAND_Sのdefineが必要でworkaroundには不適。CryptGenRandomを用いる。

#include <random>
#if defined(__MINGW32__) && defined(__GNUC__) && !defined(__clang__)
#include <system_error>
#include <limits>
#include <string>
#include <Windows.h>
#include <wincrypt.h>
namespace workaround_mingw_gcc {
class random_device {
private:
  class crypt_context {
  private:
    HCRYPTPROV prov_;
  public:
    crypt_context(DWORD prov_type, LPCTSTR container = nullptr, LPCTSTR provider = nullptr, DWORD flags = 0)
    {
      using std::system_category;
      using std::system_error;
      const auto er = ::CryptAcquireContext(&this->prov_, container, provider, prov_type, flags);
      if(!er) {
        throw system_error(
          std::error_code(::GetLastError(), system_category()),
          "CryptAcquireContext:(" + std::to_string(er) + ')'
        );
      }
    }
    crypt_context(const crypt_context&) = delete;
    void operator=(const crypt_context&) = delete;
    ~crypt_context() noexcept
    {
      ::CryptReleaseContext(this->prov_, 0);
    }
    //HCRYPTPROV& get() noexcept { return this->prov_; }
    const HCRYPTPROV& get() const noexcept { return this->prov_; }
  };
  crypt_context prov_;
public:
  using result_type = unsigned int;
  explicit random_device(const std::string& /*token*/ = "workaround_mingw_gcc ")
  : prov_(PROV_RSA_FULL)
  {}
  random_device(const random_device&) = delete;
  void operator=(const random_device&) = delete;
  //~random_device() = default;
  double entropy() const noexcept { return 0.0; }
  result_type operator()()
  {
    using std::system_category;
    using std::system_error;
    result_type re;
    const auto er = ::CryptGenRandom(this->prov_.get(), sizeof(re), reinterpret_cast<BYTE*>(&re));
    if(!er) {
      throw system_error(
        std::error_code(::GetLastError(), system_category()),
        "CryptGenRandom:(" + std::to_string(er) + ')'
      );
    }
    return re;
  }
  static constexpr result_type min() { return std::numeric_limits<result_type>::min(); }
  static constexpr result_type max() { return std::numeric_limits<result_type>::max(); }
};
}
namespace cpprefjp {
  using workaround_mingw_gcc::random_device;
}
#else //defined(__MINGW32__) && defined(__GNUC__) && !defined(__clang__)
namespace cpprefjp {
  using std::random_device;
}
#endif //defined(__MINGW32__) && defined(__GNUC__) && !defined(__clang__)

ref

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions