手書きのリソーススクリプトなら先頭で、Visual C++プロジェクトでなら「読み取り専用ヘッダーファイル」で指定するヘッダーファイルの話の続きです。前回(リソース関係のヘッダーファイルまとめ)、いろんなリソーススクリプト用のヘッダーファイルを紹介しました。今回は、私の選択基準を書きます。

MFCを使用するWTLを使用するno<afxres.h>yesVC++ 2012以上を使用するno<atlres.h>yes<winres.h>yes<winresrc.h>no

  • アプリでMFCを使っているなら、MFCの<afxres.h>を使います。
  • アプリでWTLを使っているなら、WTLに入っている<atlres.h>を使います。
  • Visual C++ 2012およびそれ以降を使っているなら、<winres.h>を使います。
  • それ以外の場合、<winresrc.h>を使います。たとえば、Visual C++ 2010までだったり、MinGWなどだったりする場合です。

もっとも、これに従うと、ほとんどは<atlres>か<winres.h>という結論になります。私は新規で作るときにMFCを使うことはありませんし、最近のバージョンのVisual C++ばっかり使っているからです。

念のため追記しておきますが、これが絶対の基準であるとは思っていません。だから、「私の選択基準」という表現にしました。

この記事のカテゴリ

私が知る、リソーススクリプト用のヘッダーファイルのまとめです。

afxres.h
MFCのヘッダーファイルです。winres.hをインクルードしています。リソースID用の定数を多数定義しています。その中にはAFX_IDW_TOOLBARなどAFXで始まるものもあれば、ID_FILE_NEWのように汎用的に使えそうなものもあります。
atlres.h
WTLのヘッダーファイルです。Visual Studioの付属品ではありません。winresrc.hをインクルードしています。afxres.hのように、リソースID用の定数を多数定義しています。ATL/WTL用にATL_IDW_TOOLBARなどATLで始まるものもあります。そのほか、VS_VERSION_INFOやIDC_STATIC、ID_FILE_NEWなど、winres.hやafxres.hと同等のものも定義されています。
winres.h
前回の記事に書いたとおり、以前はMFC扱いでしたが、今はWindows SDKに収録されています。winresrc.hをインクルードしています。定数VS_VERSION_INFOと定数IDC_STATICの定義があります。
winresrc.h, windows.h
Windows SDKあるいはPlatform SDKに昔からあります。どちらをインクルードしても同じです。リソースコンパイル時のwindows.hは、winresrc.hをインクルードするようになっているためです。

以下、細々とした話です。

  • Visual Studioのリソースエディタでダイアログにスタティックコントロールを設置すると、リソースIDがIDC_STATICになります。そのため、Visual Studioでデスクトップアプリを作るなら、windows.hやwinresrc.hでは足りません。それ以外を使用するのが良いでしょう。
  • 最近のVisual Studioでは、デフォルトのオプションだとMFCが入りません(C++自体もそうですけど)。MFCが入っていないということはafxres.hもありません。アプリ自体でMFCを使っていない場合、afxres.h以外を使うのがおすすめです。
  • winres.hはWindows SDKにはありますが、現在のところMinGW-w64にはないようです: /mingw-w64-headers (Tag v5.0.1)。

このラインナップ、私が認識しているものを並べたものです。なので、Visual StudioやWindows SDKに入っていないatlres.hを含めています。

この記事のカテゴリ

リソース関係のヘッダーファイルwinres.hの話です。これはMFCのヘッダーファイルだったはずですが、少し前からWindows SDKに収録されるようになりました。

(さらに…)

この記事のカテゴリ

以下のコードのように、実際には何も行わず、ただミューテックスの要件を満たすだけの実装は簡単に作れるでしょう。Null Objectパターンの一種と言えますよね。

class null_mutex
{
  void lock() {}
  void try_lock() {}
  void unlock() {}
};

Boostには、そんなクラスがなんだかあちこちにあるようです。

Boost.Threadのものが最も汎用的です。BoostのUpgradeLockableコンセプトまで実装しています。それに次ぐのがBoost.Interprocessのものです。残りはLockableコンセプト(lock, try_lock, unlock)のみ実装のようです。

私自身は、boost::signals2::dummy_mutexなら使ったことがあります。シングルスレッドでboost::signals2::signalを使うという状況でした。

この記事のカテゴリ

  • ⇒ Boostのなんにもしないミューテ(ッ)クス

Visual C++ 2015および2017 RCでは、<codecvt>の各クラステンプレートにchar16_tやchar32_tを組み合わせるとリンクエラーになるという問題があります。今回、これに対する条件付きのWorkaroundについて書きます。

まずは、エラーになるコードの例を提示します。

#include <codecvt>
#include <string>
#include <cassert>
#include <locale>
 
int main()
{
  std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter_utf32;
  std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter_utf16;
 
  std::string u8str = u8"\U0001F359";
 
  std::u32string u32str = converter_utf32.from_bytes(u8str);
  std::u16string u16str = converter_utf16.from_bytes(u8str);
}

これをVisual C++ 2015でコンパイル・リンクするとこんなエラーメッセージが出てきます。

u.obj : error LNK2019: 未解決の外部シンボル "public: static class std::locale::id std::codecvt::id" (?id@?$codecvt@_SDU_Mbstatet@@@std@@2V0locale@2@A) が関数 "public: __thiscall std::locale::locale >(class std::locale const &,class std::codecvt_utf8_utf16 const *)" (??$?0V?$codecvt_utf8_utf16@_S$0BAPPPP@$0A@@std@@@locale@std@@QAE@ABV01@PBV?$codecvt_utf8_utf16@_S$0BAPPPP@$0A@@1@@Z) で参照されました。
u.obj : error LNK2019: 未解決の外部シンボル "public: static class std::locale::id std::codecvt::id" (?id@?$codecvt@_UDU_Mbstatet@@@std@@2V0locale@2@A) が関数 "public: __thiscall std::locale::locale >(class std::locale const &,class std::codecvt_utf8 const *)" (??$?0V?$codecvt_utf8@_U$0BAPPPP@$0A@@std@@@locale@std@@QAE@ABV01@PBV?$codecvt_utf8@_U$0BAPPPP@$0A@@1@@Z) で参照されました。
u.exe : fatal error LNK1120: 2 件の未解決の外部参照

さて、エラーメッセージを読むと、1件目はstd::locale::id型のstaticメンバー変数std::codecvt<char16_t,char,struct _Mbstatet>::idが見つからないという内容です。2件目も同じ内容でchar32_tになっているだけです。ということは、これを自分で定義したら良いのではないでしょうか?

というわけで、こんなコードを書いたらうまくいきました。コンパイル・リンクできてちゃんと実行できました。

#include <codecvt>
#include <string>
#include <cassert>
#include <locale>
 
// この2行を追加
std::locale::id std::codecvt<char32_t, char, std::mbstate_t>::id;
std::locale::id std::codecvt<char16_t, char, std::mbstate_t>::id;
 
int main()
{
  std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter_utf32;
  std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter_utf16;
 
  std::string u8str = u8"\U0001F359";
 
  std::u32string u32str = converter_utf32.from_bytes(u8str);
  std::u16string u16str = converter_utf16.from_bytes(u8str);
}

ただし、この方法を使うには条件があります。それは、VC++ランタイムを静的リンクする場合(/MT/MTdコンパイルオプション)だけ使えるというものです。/MD/MDdだと、問題となるstaticメンバー変数idが__declspec(dllimport)付きの宣言となってしまうため、この方法が使えません。別のリンクエラーになります。

ところで、以下のcpprefjpの各ページのサンプルコードもこの問題に該当します。それぞれのサンプルコードの作成・動作確認にあたり、私はこの方法で乗り切りました。

まとめです。

  • codecvt_utf16, codecvt_utf8, codecvt_utf8_utf16のいずれかに、char16_tかchar32_tを使うとリンクエラーが出る。
  • そうなったら、codecvtクラステンプレートののstaticメンバー変数idを自分で定義すれば、リンクが通る。

バグ報告は出ているそう(Visual C++における文字コード変換 – C++と色々)なので、将来的にはVisual C++が直ることを期待します。あと/MD, /MDdで使えるWorkaround欲しいです。

この記事のカテゴリ

  • ⇒ VC++ 2015のcodecvtでリンクエラーになる問題の回避策

Visual C++ 2015と2017 RCで、以下のプログラムが期待どおりに動きませんでした。

#include <iostream>
#include <sstream>
#include <iomanip>
#include <ctime>
 
int main()
{
  std::istringstream s("20170110");
 
  std::tm x = {};
  s >> std::get_time(&x, "%Y%m%d");
 
  char buf[255];
  strftime(buf, sizeof(buf), "%Y-%m-%d", &x);
  std::cout << buf << std::endl;
}

%Yで2017だけパースされることを期待しているのですが、Visual C++の実装では、20170110まで読み込んでしまうようになっていました。

libstdc++やlibc++では、ちゃんと2017だけ読み込む実装だったので、Visual C++のほうに問題がありそうです。

2017年1月15日追記: 同じ問題が報告されているのを見つけました。std::get_time fails when using formats without separators – Developer Community

この記事のカテゴリ

  • ⇒ VC++でstd::get_timeがなんかダメ
  • ⇒ VC++でstd::get_timeがなんかダメ

主にLinuxで使われるGNU C Library (glibc)では、wchar_tをUCS-4 (UTF-32)固定としている、そう私は認識しています。しかし、ずっとその根拠を確かめずにそう思い込んでいる状態だったので、気になって調べました。

マニュアルにそのような記載が無いかと探した結果、The GNU C Library: Selecting the Conversionに以下の記述を見つけました。

The wide character set is always UCS-4 in the GNU C Library.

このほか、LinuxのMan page of UNICODEには、以下の記述があります。

GNU/Linux では、C 言語の型 wchar_t は符号付き 32 ビット整数型である。 その値は C ライブラリにより (すべてのロケールにおいて) 常に UCS コードの値として解釈される。 これを GNU C ライブラリがアプリケーションに知らせるための規約として、 定数 __STDC_ISO_10646__ を定義する。 これは ISO C99 規格で指定されている。

なお、__STDC_ISO_10646__を定義している箇所も見つけました。Move __STDC_* predefined macros from features.h to stdc-predef.h. · git-mirror/glibc@ff3b3d8 · GitHubです。

というわけで、glibcにおいてwchar_tは常にUCS-4である、という認識で良さそうです。

この記事のカテゴリ

  • ⇒ glibcのwchar_tがUCS-4であることを確認した
  • ⇒ glibcのwchar_tがUCS-4であることを確認した

次の表を見てください。<time.h>あるいは<ctime>でtime_tとtm構造体との変換を行う関数をまとめました。

time_t→tm構造体 tm構造体→time_t
現地時刻 localtime mktime
UTC gmtime

表の「?」の部分、UTCでtm構造体からtime_tに変換する関数、それがCおよびC++の標準ライブラリにはありません。

仕方ないので、非標準でもいいから何かないかと探しました。どうやら、Unix系の一部にはtimegm、Visual C++には_mkgmtimeという関数があることが分かりました。

Linux glibc, BSD系列, Cygwinなど
timegm関数 (Linux, FreeBSD)
Visual C++
_mkgmtime

このほか、Linuxのmanには、Unix系一般に通用しそうな方法が説明されています。setenvで環境変数TZを設定のうえ、mktimeを呼び出すというやり方です。

なおこれ、std::chrono::system_clock::time_point ←→ std::time_t ←→ std::tm ←→ (std::put_time, std::get_time)というstd::chrono::system_clock::time_pointからの入出力の過程で必要になったので、調べました。

この記事のカテゴリ

  • ⇒ tmからtime_tにUTCで変換する関数を探した
  • ⇒ tmからtime_tにUTCで変換する関数を探した

この記事は、初心者C++er Advent Calendar 2016の23日目の記事です。


WindowsのVisual C++でこんなコードを書き始めたんです。

#include <cstdint>
#include <windows.h>
 
void f(std::uint8_t) {}
void f(std::uint16_t) {}
void f(std::uint32_t) {}
void f(std::uint64_t) {}

符号無し整数型をすべて網羅するよう、関数fを多重定義したわけです。さらにコードを書き進めます。

int main()
{
	DWORD x = 12345;
	f(x);
}

こんなコードを書いたら、コンパイルエラーになってしまいました。

t.cpp
t.cpp(12): error C2668: ‘f’: オーバーロード関数の呼び出しを解決することができません。(新機能 ; ヘルプを参照)
t.cpp(7): note: ‘void f(uint64_t)’ の可能性があります
t.cpp(6): note: または ‘void f(uint32_t)’
t.cpp(5): note: または ‘void f(uint16_t)’
t.cpp(4): note: または ‘void f(uint8_t)’
t.cpp(12): note: 引数リスト ‘(DWORD)’ を一致させようとしているとき

cstdintの中のstdint.hとwindows.hを見て原因を突き止めました。

  • uint32_tはunsigned intからのtypedef。
  • 一方、DWORDはunsigned longからのtypedef。

どうやら、コンパイラはunsigned longをunsigned intにするかunsigned long longにするか、決められなかったということのようです。

このように、整数型を大きさごとに多重定義するのは難しいようです。

この記事のカテゴリ

  • ⇒ サイズ別に整数型を多重定義したかった
  • ⇒ サイズ別に整数型を多重定義したかった

この記事は、C++ Advent Calendar 2016の12日目の記事です。


std::lock_guardクラステンプレートは、std::mutexなどのlockとunlockをRAII化するものとして欠かせません。

std::mutex m;
std::lock_guard<std::mutex> guard(m);

しかし、このようにテンプレート実引数としてミューテックスの型を指定しなければならないのが少しです。そこで、Boost.Thraedの中からこの点少し便利になったものを2つ紹介します。どれもヘッダーのみ、ビルド不要で使えます。

make_lock_guard

まずは、lock_guardオブジェクトを作るboost::make_lock_guard関数テンプレートです。std::make_pairなどのように、よくある関数テンプレートです。ただし、ミューテックスやlock guardは要件(コンセプト)でコピー・ムーブ可能となっていないので、このように参照で受ける必要があります。

std::mutex m;
auto&& guard = boost::make_lock_guard(m);
// 以下も可。
// const auto& guard = boost::make_lock_guard(m);

なお、boost::make_unique_lockとboost::make_unique_locksもあります。boost::make_unique_locksは複数個のミューテックスを一挙にロックします。内部では、boost::lock関数を呼び出しています。

std::mutex m1
std::recursive_mutex m2;
auto locks = boost::make_unique_locks(m1, m2);
// locksの型はstd::tuple<std::mutex, std::recursive_mutex>

なお、boost::adopt_lockを実引数として受け取るboost::make_lock_guardや、boost::adopt_lock, boost::defer_lock, boost::try_to_lockのそれぞれを実引数として受け取るboost::make_unique_lockもあります。

以下、Boostのリファレンスページへのリンクです。

with_lock_guard

次は、boost::with_lock_guard関数テンプレートです。これは、ミューテックスをロックしてから、指定の関数オブジェクトを呼び出すというものです。もちろん、関数オブジェクトから脱出したら、ロック解除されます。

std::mutex m;
int x = boost::with_lock_guard(m, [&]{
  // ロックされている状態。
  // いろんな処理を実行する。
  return 1;
});
// xには関数オブジェクトの戻り値1が入る。
 
boost::with_lock_guard(m, [](int n) {}, 2);
// with_lock_guardの3番目以降の実引数は、関数オブジェクトに渡される。

boost::with_lock_guardの良いところは、排他制御する範囲を明確にできる点です。戻り値の受け渡しが可能なので、関数内に{}でスコープを作ってstd::lock_guardを使うコードでは代用が難しい場合でも、スマートに書ける可能性があります。

class hoge
{
public:
  // デフォルトコンストラクタがないクラス。
  explicit hoge(int) {}
  hoge(hoge&&) = default;
};
 
std::mutex m;
 
void f1()
{
  // ここで前処理
 
  hoge obj = boost::with_lock_guard(m, []
  {
    hoge tmp(3);
    // ここで何か処理
    return tmp;
  });
 
  // ここで残りの処理
}
 
// こういうコードは書けない、という例
void f2()
{
  // ここで前処理
 
  hoge obj; // デフォルトコンストラクタがないので無理!
  {
    std::lock_guard<std::mutex> guard(m);
    hoge tmp(3);
    // ここで何か処理
    obj = std::move(tmp);
  };
 
  // ここで残りの処理
}

boost::with_lock_guardを使うには、<boost/thread/with_lock_guard.hpp>をインクルードします。

以下、Boostのリファレンスページへのリンクです。


なお、boost::make_lock_guardは、C++1zで不要になります(参考:C++1z クラステンプレートのテンプレート引数推論 – Faith and Brave – C++で遊ぼう)。

今回は、ロックの補助関数に焦点を当てることにしました。そのため、synchronized_value(boost::synchronized_valueクラス – yohhoyの日記)は取り上げていません。

std::lock_guardだけでなく、これらも使って、楽してコードを書いていきましょう。

この記事のカテゴリ

  • ⇒ Boost.Threadの排他制御を補助する関数

次ページへ »