LoginSignup
39
45

More than 5 years have passed since last update.

Clang with Microsoft CodeGenがでたので試す

Last updated at Posted at 2015-12-08

C++初心者Advent Calendar 2015

この記事はC++初心者Advent Calendar 2015 9日目の記事です
<< 8日目 | C++のポインタ渡しと参照渡しの使い分け || 10日目 | テンプレートでコケた話 >>
17日目 | C99からC++14を駆け抜けるC++講座 >>

個人的には、歌舞伎座.tech #8 「C++初心者会」とか見て以降、「初心者」が怖くなってしまっていますが(初心者がBoost.asioをつかえるもんか!)、ここまでは初心者向けでものすごくホッとしています。初日の
初心者が C++ を勉強するときに最低限押さえておいたほうがよい C++11/14 の機能
を除いてですが(最近の初心者は継承が使えるのか・・・怖い)

8日目 | C++のポインタ渡しと参照渡しの使い分けとかはさっぱりしていてわかりやすいのではないでしょうか。なので見ろ(ぇ)

以下、4日目のC++を始めよう for windows - Qiitaを見ている前提で話が進みます。見てない方は先にどうぞ

追記

現在、Clang with Microsoft CodeGenのclangのバージョンが3.8のままな気がします。最新のclangのほうが絶対良いと思われるので、 @kazatsuyu さんのfafnirというツールを使うことをおすすめします。詳細は

はじめに

先日Clang with Microsoft CodeGenなるものが出た。なのでこれはなにかという話と、そもそもコンパイラって?というお話から書いていこうと思います。

で、MSの名前が出た時点で察しですが、for Windowsな記事となっています。え?Linux?Mac?おめーら、優秀なコンパイラあんだろーが。

目的

  • Clangの存在を世に広める
  • 最近はMSも頑張ってることを伝える
  • おのれMS

・・・いやですね、
C++を始めよう for windows | Qiita
で、Visual Studioの紹介自体はあったので。これは書かねばなるまいということで。
だって、gccの扱いが

gccはLinux等でよく使われるコンパイラですが、今は忘れてもかまいません。

でclangに至っては記述なしですからね、これは宣伝せざるを得ません(謎の使命感)

コンパイラとは

コンパイラとは特定言語で書かれたコードを機械言語、ないしもとより下位の言語(中間言語)に翻訳するものです。
有名なコンパイラとしては

  • gcc/g++
  • clang/clang++
  • Visual Studio付属のcl.exe
  • icc

などでしょうか。(bcc32.exeなんてなかった)

この後話すClang with Microsoft CodeGenとは細かい話を抜けば、Visual StudioをIDEとしてつかいつつ、コンパイラだけClangに差し替える、というものです。

Clang(クラン)とは

もともと、iPhoneで有名なAppleがLLVMという中間言語を経由してC/C++のコードをコンパイルするプロジェクトを立ち上げたものです。gccとちがいマルチスレッドで動作も高速、LLVMを使うことによりgccより最適化がし易いなどなどの特徴があります。なおgccがGPLv3というライセンスなのに対し、ClangはUniversity of Illinois/NCSA Open Source Licenseというライセンスで、コピーレフト絶対主義者の自由ソフトウェア主義者からは妬まれています。パルパル。

C++に関しては、後発のコンパイラにもかかわらず、もっともC++規格準拠がはやいコンパイラとなっています。まあ細かいバグはあるんですが。

これまでのWindowsでClangを利用する方法

msys2 mingw clangをつかう

多分これが一番はやいと思います。今でも。

  1. 7-Zipを落とす
    https://sevenzip.osdn.jp/
    より64bit版もしくは32bit版を
    既に入っている人も、2015/11/19に随分久しぶりに最新版が出たのでバージョンを上げるといいです
  2. 7zipをインストール
    ダブルクリックして実行すればいいです。
  3. msys2を落とす
  4. http://sourceforge.net/projects/msys2/files/Base/
    よりお使いのアーキテクチャ(おそらくはx86_64)をクリックしmsys2-base-[アーキテクチャ]-[日付].tar.xz を。exeの方は1回も使ったこと無いのでわかりません。
  5. 7-zipで解凍
  6. msys2_shell.cmdをダブルクリック、Close Windowと言われたらばってんを押して閉じる
  7. 再びmsys2_shell.cmdをダブルクリック、pacman -Syuuと打ち実行、また閉じる
  8. 再びmsys2_shell.cmdをダブルクリック、pacman -S mingw-w64-i686-clang mingw-w64-x86_64-clangとと打ち実行、また閉じる
  9. コマンドプロンプトでmsys2_shell.cmdのあるパスに移動し、call msys2_shell.cmd -mingw64(64bit向けのコンパイル)またはcall msys2_shell.cmd -mingw32(32bit向けのコンパイル)を実行mingw64(64bit向けのコンパイル)またはmingw32.exe(32bit向けのコンパイル)を実行
  10. clangコマンドが使える!

mingwを自力で入れてさらにclangを入れる

Cドライブ直下にmingwを置かないといけなくて、シンボリックリンクではダメだったので私はやっていません

Visual Studioを導入してからClangを入れる

VSが入っている状態で
http://llvm.org/releases/download.html
Clangを入れるとVisual StudioからもClang-clが使えるようになる

Clang with Microsoft CodeGen

なぜClang with Microsoft CodeGenか

こうした方法があったわけですがどれもいまいちでした。最初のmsys2を使うものは、ついでにgccも入って便利ではあるんですが、コンパイルはコマンドラインなので、makefileを書かねばならず、んなもん初心者に書けるか、というお話です。かと言ってVSをいれてからClangを入れる方法ははっきり言って実験段階というか、VSとの連携が不十分でした
そして、最大のメリットは、Visual Studioの唯一の利点、優秀なデバッグ機能がフルに使えるということです。あのデバッグ機能がなければ、誰ひとりとしてあんな糞コンパイラ使ってないでしょう。

Clang with Microsoft CodeGenの仕組み

ではこのClang with Microsoft CodeGenはどのようにコンパイルがなされるのでしょうか。

コンパイラ名 flow
mingw clang ソースコード→LLVM IR→実行ファイル
VS default ソースコード→MS独自の中間言語→実行ファイル
Clang with Microsoft CodeGen ソースコード→LLVM IR→MS独自の中間言語→実行ファイル

こんな感じです。(だれか図をください)
なお

Visual StudioにClangフロントエンドがやってくる
http://cpplover.blogspot.jp/2015/10/visual-studioclang.html
MS独自のプロプライエタリなTwo-phase lookupすらまともにできない規格違反のクソC++コンパイラーの開発も続行されるらしい。

某友人はこれを聞いて思わずFワードを吐いていました。大丈夫だ、気持ちはわかる。

以下の内容を動画化した

ぶっちゃけわかりにくいので、動画にしました
【ゆっくり実況】Clang with Microsoft CodeGenの導入実況 - ニコニコ動画:GINZA
どうぞ御覧ください。(宣伝乙とか言わない)

じゃあ導入するか

  1. https://www.visualstudio.com/ja-jp/downloads/download-visual-studio-vs.aspx に行き、 ダウンロード  Visual Studio.png
    ダウンロードする
    そして実行する
    カスタムインストールか否かが出たらカスタムインストールに。
  2. 選択画面が出たら

    クロスプラットフォームモバイル開発Visual C++モバイル開発Clang with Microsoft CodeGen(2016年1月)にチェックをつける

    プログラミング言語Visual C++にチェックが付いているか確認する
    無題.png

  3. インストール。気長に待つ

どうやって使うか

新しいプロジェクト→インストール済み→Visual C++→クロスプラットフォーム
にstatic/dynamic libraryをClangで作れるものがありますが、そんなもの初心者はお世話にならないので

http://qiita.com/cpp_maid_BOT/items/13eab3a134e3f6794f07
「新しいプロジェクト > Visual C++ > Win32コンソールアプリケーション」で名前を適当に
アプリケーションの種類を「コンソールアプリケーション」追加のオプションを「空のプロジェクト」にチェックを入れ完了します
そして「ソリューションエクスプローラーからソースファイルを選び右クリック > 追加 > 新しい項目 > C++ファイル名前mainで追加」

でいいです(ちなみに[Ctrl]+[Shift]+[A]なんて便利なショートカットもあるんやで。)

ここからが大事です

  1. プロジェクト→xxxのプロパティ→構成マネージャを開く
  2. アクティブソリューション構成→新規作成で、設定のコピー元が空の状態で適当な名前をつけて構成を作成します。私はclang_Debugclang_Releaseの2つを追加しています。
  3. 構成マネージャを閉じます
  4. そのままプロパティページが開いていると思うので、プラットフォームをx64すべてのプラットフォームに、構成→複数の構成で先ほど作った構成のみチェックをつけます
  5. 構成プロパティ→全般
    にある
    プラットフォームツールセット

    Clang 3.7 with Microsoft CodeGen (v140_clang_3_7)
    Visual Studio 2015 - Clang with Microsoft CodeGen (v140_clang_c2)
    にします
  6. 構成プロパティ→C/C++→コード生成→C++の例外を有効にする を「はい」に。(テーブルをアンウィンドってなんだ)Clang with Microsoft CodeGen (January 2016) でデフォルトで有効になりましたね
  7. 構成プロパティ→C/C++→全般→デバッグ情報の形式で完全なデバッグ情報 (DWARF2) (-g2 -gdwarf-2)を選択

あとはコード書くだけです。

コードは
http://faithandbrave.hateblo.jp/entry/20131119/1384837618
より。

C++14_constexpr
constexpr void square(int& x)
{
    x *= x;
}

constexpr int f(int x)
{
    square(x);
    return x;
}

こんなコードももちろん動きます。あとは
https://gist.github.com/yumetodo/dad352ffdc27c19cf825
こんなコードとか。

注意

IntelliSense

IntelliSenseは対応していません。いっその事IntelliSenseを切ってもいいかもしれません。

Windows.hについて

追記:いつの間にやら修正されました。

多分7月版からだと思うんだけど、Windows.hをincludeすると

Windows Kits\8.1\Include\um\combaseapi.h(229,21): error : unknown type name 'IUnknown'
static_cast<IUnknown*>(*pp);    // make sure everyone derives from IUnknown

とか怒られるようになりました。というわけで使うときは

#if !defined(CINTERFACE) && defined(__c2__) &&  __clang_major__ == 3 && __clang_minor__ == 8
//To avoid compile error
//C:\Program Files (x86)\Windows Kits\8.1\Include\um\combaseapi.h(229,21): error : unknown type name 'IUnknown'
//          static_cast<IUnknown*>(*pp);    // make sure everyone derives from IUnknown
struct IUnknown;
#endif
#include <Windows.h>

としましょう。

詳細
Clang with Microsoft CodeGen(2016/07版)でWindows.hを使うときに注意すべきこと
http://qiita.com/yumetodo/items/a8c408078766a5c81e31

asm

https://blogs.msdn.microsoft.com/vcblog/2015/12/04/clang-with-microsoft-codegen-in-vs-2015-update-1/#known-issues-in-the-first-preview
No inline asm on any architecture. You will get a diagnostic that says “Inline assembly is not supported” or “GNU-style inline assembly is disabled”

アセンブリ使えません。全滅です。SIMDとかは試してないけど手動ではできないんじゃね?今後に期待。

試しましたが、そもそも__cpuidが使えないです。
ちゃんとc:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\Clang 3.7\include\Intrin.h

Intrin.h_l.74
static __inline__
void __cpuid(int[4], int);
static __inline__
void __cpuidex(int[4], int, int);

とかいてあるにもかかわらず

error LNK2019: 未解決の外部シンボル "void __cdecl __cpuid(int * const,int)" (?__cpuid@@YAXQAHH@Z) が関数 "struct regs_t __cdecl get_cpuid(unsigned int)" (?get_cpuid@@YA?AUregs_t@@I@Z) で参照されました。

おい、どういうことだ、Link errorにするくらいなら最初っからヘッダーつけんなばーか。しかもヘッダーでは

Intrin.h_l.56
#ifdef __cplusplus
extern "C" {
#endif

されてんのにlink error messageが?__cpuid@@YAXQAHH@Zってお前何を探してやがるんだ

と言った状況からはClangが3.8にあがった5月版から改善したようで、例えば__cpuidは使えるようになりました(GNU Styleの5引数の__get_cpuidのみで__cpuidはただのalias)。

SIMD Intrinsic は相変わらず使えません。というのも、コンパイラにIntrinsic not yet implementedとか言われます
ヘッダー見てる感じVS-Style、GNU-styleどっちも使えそうだったから(例えば_rdrand32_step__builtin_ia32_rdrand32_step)行けるのかとおもいきやダメでした。-msse2とか-mrdrndとか-march=nativeも効きませんでしたとさ。

tchar

Clang with Microsoft CodeGenを使う際、tchar.hをWindows.hより先にincludeしてはならない

なぜかというと、

C:\Program Files (x86)\Windows Kits\10\Include\10.0.10240.0\ucrt\tchar.h

がincludeされるのだが、

tchar.h_175-180行目
#ifndef _TCHAR_DEFINED
#if !__STDC__
typedef wchar_t     TCHAR;
#endif  /* !__STDC__ */
#define _TCHAR_DEFINED
#endif  /* _TCHAR_DEFINED */
tchar.h_1261-1266行目
#ifndef _TCHAR_DEFINED
#if !__STDC__
typedef char            TCHAR;
#endif  /* !__STDC__ */
#define _TCHAR_DEFINED
#endif  /* _TCHAR_DEFINED */
tchar.h_2090-2095行目
#ifndef _TCHAR_DEFINED
#if !__STDC__
typedef char            TCHAR;
#endif  /* !__STDC__ */
#define _TCHAR_DEFINED
#endif  /* _TCHAR_DEFINED */

のようになっていて、TCHAR型が使えない。なぜならClang with Microsoft CodeGenでは__STDC__マクロがdefineされるからだ。逆に言えば、MSVCは

https://msdn.microsoft.com/ja-jp/library/b0084kay.aspx
ANSI/ISO C99 規格への準拠を示します。 /Za コンパイラ オプションが指定され、C++ コードをコンパイルしていない場合のみ、整数リテラルの定数 1 として定義されます。それ以外の場合は、定義されません。

と、定義されていないので使えるのである。しかも

#define _TCHAR_DEFINED

の一文のせいで、例えばWindows.hをincludeした時にこっちのTCHAR型まで無効化するのである。おのれMS。

コメントで、コンパイラオプションでマクロ__STDC__を0にdefineするといった方法が紹介されています。

https://connect.microsoft.com/VisualStudio/Feedback
http://blogs.msdn.com/b/vcblog/archive/2015/12/04/introducing-clang-with-microsoft-codegen-in-vs-2015-update-1.aspx
どっちにバグレポ投げればいいの?

https://connect.microsoft.com/VisualStudio/feedback/details/2122400
(いつの間にかMSのバグ報告の場所が変わってしかもURL死にやがったのでWeb Archiveでどうぞー)
MSにバグレポ投げました。この英語力のなさ、どうにかしたい。

追記

投稿者: James [MSFT]、投稿日時: 2016/01/12 6:06
Hello,

Thank you for reporting this issue. We've fixed this; the fix will appear in a future release of the Windows SDK.

Sincerely,

James McNellis
Visual C++ Libraries
james.mcnellis@microsoft.com

ほう、fixされるらしいぞ。やったね。でもWindows SDKの更新ってそれいつよ。

Debug

ちゃんとステップ実行もできます。できるんですが!逆アセンブルウィンドウも使えますが!何故にや知らん、スタックトレースしてくれません。辛い。-gつけてコンパイルしてませんでした、ちゃんと動きます

例外

http://blogs.msdn.com/b/vcblog/archive/2015/12/04/introducing-clang-with-microsoft-codegen-in-vs-2015-update-1.aspx
When changing from MSVC toolset, exception handling is currently off-by-default even though exception handling works. The developer can override this switch manually. The default will change in the next release.

例外を使うコードはデフォルト設定では使えない。上にも書いたとおり、構成プロパティ→C/C++→コード生成→C++の例外を有効にする を「はい」にする必要がある。次のリリースではデフォルトで有効になるようですが。

またx86(Win32)アーキテクチャの例外処理はバグが多いと誰かが言っていたので避けたほうが賢明かもしれない。

'_Ty' does not refer to a value

Visual Studio 2017(_MSC_VER1911らへん)の場合、

In file included from C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.12.25827\include\exception:7:
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.12.25827\include\type_traits(898,47): error : '_Ty' does not refer to a value
                : bool_constant<__is_trivially_destructible(_Ty)>
                                                            ^
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.12.25827\include\type_traits(896,16) :  note: declared here
template<class _Ty>
               ^
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.12.25827\include\type_traits(899,2): error : expected class name
        {       // determine whether _Ty has a trivial destructor
        ^
2 errors generated.

のようなエラーが出ます。

これはClang with Microsoft CodeGenのclangがVSのコンパイラ拡張の__is_trivially_destructibleに対応していないため。新しいclangを自力で入れて使えば大丈夫っぽいです。

Breaking Change

プラットフォームツールセットの名前がClang 3.7 with Microsoft CodeGen (v140_clang_3_7)からVisual Studio 2015 - Clang with Microsoft CodeGen (v140_clang_c2)に変更になっています。プロジェクトの設定を変えないとコンパイルできなくなっていますのでご注意。

結論

  • Clang with Microsoft CodeGenの導入に成功し、それを記事にしたことでClangの存在を世に広める事ができた(?)
  • Clang with Microsoft CodeGenという取り組みを伝えることで、最近はMSも頑張ってることを伝えることができた
  • おのれMS
  • __cpuid使わせろ

C++初心者Advent Calendar 2015

この記事はC++初心者Advent Calendar 2015 9日目の記事です
<< 8日目 | C++のポインタ渡しと参照渡しの使い分け || 10日目 | テンプレートでコケた話 >>
17日目 | C99からC++14を駆け抜けるC++講座 >>

次はringoh72さんのテンプレートでコケた話ですね。今日も私はtemplateには苦しめられたので楽しみです。よろしくお願いします。

39
45
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
45