highlight

2016年8月30日火曜日

WoWJapanizer を Legion expansion で使えるようにする

昨日のエントリで、とりあえず WoWJapanizer がエラーを出さないようにしたが、もう少しまともな変更を施す事にする。

GlyphFrame がエラーを出していた件については現 version では Glyph 変更用のフレームが廃止されたそうなので、 バッサリとコードを除去する、という対応で問題なさそうだ。ただ minor glyph は残るそうなので、 この対応をどうすればいいのか、WoW 自体久々な私にはちょっと良くわからず、必要があれば又の機会に対応する事にしよう。

あとの二つは Interface Option の設定を取得する方法が変更された様なので、現行仕様に沿う(と思しき)方法に書き換える。多分 InterfaceOptions~ 変数が使えなくなったのだろう。 GetCVar() の引数に直接 CVar の名前を与えることにする。

  • InterfaceOptionsHelpPanelEnhancedTooltips を使っているところでは、"UberTooltips"
  • InterfaceOptionsDisplayPanelShowSpellPointsAvg を使っているところでは、"SpellTooltip_DisplayAvgValues"

これで Interface option の該当箇所のチェックを on/off したときに、その振る舞いが変わる様になると思う。 が、元のコードの意図をキチンと汲み取っているかどうかについては、ちょっと良くわからない。

以下、patch ファイルを掲示しておく。cygwin を使っている人なら、World of Warcraft/Interface/Addon/WoWJapanizer ディレクトリに mv して、

$ patch -p1 -u < 以下のパッチファイル

を実行すればいい。試してないけど多分パッチが当たる。 mac の人はよく知らないけど、中身が bsd だそうだから patch コマンドは普通に使えるんじゃないかな? cygwin とか patch が入ってないなら、以下の内容をみて、直接ファイルを書き換えると良いです。 ざっと見たら想像つくと思いますが、該当する .lua ファイルを見つけて、- の行を消して + の行で置き換えて行けばいいです。

diff -ur WoWJapanizer.orig/Data/Item/WoWJapanizer_Item.lua "../Blizzard/World of Warcraft/Interface/AddOns/WoWJapanizer/Data/Item/WoWJapanizer_Item.lua"
--- WoWJapanizer.orig/Data/Item/WoWJapanizer_Item.lua 2015-06-24 11:22:02.000000000 +0900
+++ "../Blizzard/World of Warcraft/Interface/AddOns/WoWJapanizer/Data/Item/WoWJapanizer_Item.lua" 2016-08-30 23:14:43.177488500 +0900
@@ -12,10 +12,7 @@
     local text = self.Data[id][1]
 
     if self.Data[id][2] then
-        local checkbox = InterfaceOptionsDisplayPanelShowSpellPointsAvg
-        checkbox.cvar = "SpellTooltip_DisplayAvgValues"
-
-        if tonumber(GetCVar(checkbox.cvar)) == 1 then
+        if tonumber(GetCVar("SpellTooltip_DisplayAvgValues")) == 1 then
             local n = {}
             for k, v in pairs(self.Data[id][2]) do
                 local i1, i2 = v:match('(%d+)-(%d+)')
diff -ur WoWJapanizer.orig/WoWJapanizer.toc "../Blizzard/World of Warcraft/Interface/AddOns/WoWJapanizer/WoWJapanizer.toc"
--- WoWJapanizer.orig/WoWJapanizer.toc 2015-06-24 11:22:02.000000000 +0900
+++ "../Blizzard/World of Warcraft/Interface/AddOns/WoWJapanizer/WoWJapanizer.toc" 2016-08-29 22:10:18.188131500 +0900
@@ -1,4 +1,4 @@
-## Interface: 60200
+## Interface: 70000
 ## Title: WoWJapanizer
 ## Notes: Translate Quest data, Item ToolTips and Spell ToolTips into Japanese text.
 ## Version: 3.0.0
diff -ur WoWJapanizer.orig/WoWJapanizerGlyphToolTip.lua "../Blizzard/World of Warcraft/Interface/AddOns/WoWJapanizer/WoWJapanizerGlyphToolTip.lua"
--- WoWJapanizer.orig/WoWJapanizerGlyphToolTip.lua 2015-06-24 11:22:02.000000000 +0900
+++ "../Blizzard/World of Warcraft/Interface/AddOns/WoWJapanizer/WoWJapanizerGlyphToolTip.lua" 2016-08-29 22:40:23.687407600 +0900
@@ -4,44 +4,6 @@
 WoWJapanizerGlyphToolTip.GlyphStore = nil
 
 function WoWJapanizerGlyphToolTip:OnEnable()
- hooksecurefunc("GlyphFrame_LoadUI", function()
-  for _, button in next, GlyphFrameScrollFrame.buttons do
-   button:HookScript("OnEnter", function(_button)
-    if _button.isHeader then
-     return
-    end
-
-    local name, glyphType, isKnown, icon, glyphID = GetGlyphInfo(_button.glyphIndex)
-
-    if not WoWJapanizer.db.profile.spell.tooltip then return end
-    if not WoWJapanizer:LoadAddOn("WoWJapanizer_Spell") then return end
-
-    local spell = WoWJapanizer_Spell:GetGlyphByName(name)
-
-    self:OnShow(spell, glyphID)
-   end)
-
-   button:HookScript("OnLeave", function(_button)
-    self:OnHide()
-   end)
-  end
-
-  hooksecurefunc("GlyphFrameGlyph_OnEnter", function(_button)
-   local enabled, glyphType, glyphTooltipIndex, glyphSpell, icon = GetGlyphSocketInfo(_button:GetID())
-
-   if not glyphSpell then return end
-   if not WoWJapanizer.db.profile.spell.tooltip then return end
-   if not WoWJapanizer:LoadAddOn("WoWJapanizer_Spell") then return end
-
-   local spell = WoWJapanizer_Spell:Get(glyphSpell)
-
-   self:OnShow(spell, nil)
-  end)
-
-  hooksecurefunc("GlyphFrameGlyph_OnLeave", function(_button)
-   self:OnHide()
-  end)
- end)
 end
 
 function WoWJapanizerGlyphToolTip:OnShow(spell, glyphID)
diff -ur WoWJapanizer.orig/WoWJapanizerToolTip.lua "../Blizzard/World of Warcraft/Interface/AddOns/WoWJapanizer/WoWJapanizerToolTip.lua"
--- WoWJapanizer.orig/WoWJapanizerToolTip.lua 2015-06-24 11:22:02.000000000 +0900
+++ "../Blizzard/World of Warcraft/Interface/AddOns/WoWJapanizer/WoWJapanizerToolTip.lua" 2016-08-30 23:12:59.041724400 +0900
@@ -170,9 +170,7 @@
 end
 
 function WoWJapanizerToolTip:CheckEnhancedTooltips()
-    local checkbox = InterfaceOptionsHelpPanelEnhancedTooltips
-    checkbox.cvar = "UberTooltips"
-    if tonumber(GetCVar(checkbox.cvar)) == 1 then
+    if tonumber(GetCVar("UberTooltips")) == 1 then
         return true
     else
         return false

久々の Wow で addon を使うときの話

World of Warcraft に復帰しようかなと思っている。

2年以上放ったらかしていたり、利用端末を入れ替えていたりするので、使えなくなった Addon もある。Bliz のデフォルトの UI では遊べない体になっているので、どうにかしないとやる気が出ない。 大体は Curse client でアップデートできるので問題ないが、使えなくなってしまった Addon や、使うまでに一手間かかるものもあるので、その時のメモ。

バージョン番号の更新は、*.toc ファイルの一行目 ## Interface: のあとの数値を 70000 にすれば、とりあえず Legion の最初(2016/08/30)のバージョンで outdated になることは無いので、70000 未満だったら書き換えておくこと。

Wowjapanizer

Legion 用にはアップデートされていないようだ。エラーがでて鬱陶しいが、日本語のテキストは表示されているようだ。とりあえずエラーが出ないよう、エラーが出る部分は削除かコメントアウトしとけばいいか。.toc ファイルのバージョンも 70000 にしておく。

  1. WoWJapanizerToolTip.lua の CheckEnhancedTooltips() は return false だけを残してあとはコメントアウトする。
  2. WoWJapanizerGlyphToolTip.lua の OnEnable() の中身を空にする。
  3. Data/Item/WoWJapanizer_Item.lua の Get(id) の if self.Data[id][2] then の中身、else 以降は残して、そこ以外を除去する。

これでとりあえずエラー表示無しで、日本語のクエストログが表示されるようになった。他にもエラーが出るかもしれないが、その都度対応すれば良かろう。 根本的に解決したいが、GryphFrame というのが何なのか、まだよく分かっていない。

SavedVariables

Bartender4, Shadow Unit Frame を使っていたが、これを設定し直すのは面倒なので、古いディスクから設定ファイルを持ってくる。設定ファイルは WTF/Account/<アカウント名>/SavedVariables 以下の *.lua ファイルなので、これをコピーすればそのまま使える。画面サイズなどが異なっていると、ずれたりするのかもしれない。

WowPro

Waypoint の矢印を表示するには、TomTom というアドオンをインストールしておく必要があるというのは完全に忘れていた。予めインストールしておく事。

Prat

Prat の fonts ディレクトリに使いたいフォントファイルをコピーしてから、以下の行を fonts.lua に追加する。
LSM:Register("font", "<フォント名>", [[Interface\Addons\]] .. folder .. [[<フォントのファイル名>]])

2016年8月26日金曜日

Overwatch 等ネットゲームでラグが発生する件について

新たに組み立てた i6700k + GTX 1080 マシン、頗る快調だと思っていたが、Overwatch や World of warcraft 等で遊んでいると、やけに動作が引っ掛かる。mob やプレイヤキャラがワープしまくる感じで非常に遊びづらい。

Nvidia Experience のプロファイルで最適化した設定では負荷が掛かり過ぎるのかと思っていたが、Doom なんかはヌルヌル動いているし、フレームレイトが落ちている訳でもない感じだった。 じゃあラグか?とも思うがネットワークは前に使っていた端末の LAN ケーブルをそのまま差し替えている。そして以前にはこんなラグは発生していなかった。ネットワークが遅い訳でも無さそうに思っていた。

じゃあなんで?と一週間ぐらい漠然と考えていたが、ふとデスクトップに Game First III というアイコンがあるのを見て、そういえばこのマザーボード Asus Z170 Pro Gaming には、ネットワークの帯域をゲームのパケットを優先的に利用させるという触れ込みの機能がついていたのを思い出した。 ああ、これで設定すればいいのかなと思い、使ってみたのだが、そもそも Overwatch とかのゲームを直接選択できない。 これどうやって使うんだろう?と思い、ググってみたら、Game First を使うとラグが発生するからそもそも使うべきじゃない。という英語圏の記事が割と沢山検索できてしまった。 そんなアホな、と思いながらこのサービスを停止させて Overwatch で遊んでみると、確かに敵がワープしたりしない。ホントに解消されてる・・・

このマザーボードの機能は切っておくのが正解やったんや・・・

具体的には、

  • タスクマネージャのサービスタブで、AsusGameFirstService を探してこれを停止にする。

これで上手く問題が解消されたら、次回起動時にもこのサービスは起動しないようにしておこう。

  1. 同じくタスクマネージャのサービスタブで、画面下部にあるサービス管理ツールを開くをクリックして、サービス設定アプリケーションを起動する。
  2. AsusGameFirstService をダブルクリックか右クリック > プロパティでプロパティ画面を開く。
  3. スタートアップの種類を「無効」に変更する。

2016年8月25日木曜日

i7-6700K機を組み立てる

5年ほど i7-2600 機を使っていた。Windows10 にアップグレードして一月ほどたったある日、クラッシュして復帰したら、ログインしても自分のデスクトップが現れず(アイコン表示されず、背景も真っ黒)、タスクバーも出てこない。

辛うじて、Ctrl+Shift+Esc キーでタスクマネージャは起動するので、そこからコマンドを実行することはできるので、なんとか使えなくもないのだが、きわめて不便だ。 色々復帰は試みたが元には戻せず、そろそろマシンの更新したい気持ちもあって、再インストールするくらいなら、新しいマシンを作ってやろうということでパーツを調達し始めた。ここ暫くは BTO で良いかと思って BTO PC を購入していたので、組み立ては 10 年ぶり位になると思う。

まず GPU を選定する。RX480 にするつもりだったが、229$ のはずが国内価格が 35,000 円位になっている。それにどうもかなり電力消費が大きく爆熱というレビューが多いので、nVidia のものを調べてみると、GTX 1080/1070 も国内価格は元値より二万円程高く設定されている。

国内での調達はあまりにもアホらしいので、MSI GeForce 1080 Armor 8G OC を Amazon.com で購入した。これはまだ手元に届いてないので、現時点では吉と出るか凶と出るか不明だが、少なくともボッタクられている不愉快感は払拭できる。

GPU 以外のパーツは調達してきたが、GPU が来るまでは大した作業はさせないので、のんびり組立・調整していこうと思う。

旧マシンのベンチマーク

3D Mark - Fire Strike

Graphics score
4617
Physics score
8313
Combined score
1779
Over all
4224

組み立て完了

今の PC ってめっちゃ組み立て易くなっていて、かなり久々な私がじっくり作業しても、二、三時間で組み立てが一通り完了した。 まあ、気を付ける必要があるのは CPU とそのクーラーの取り付け位で、結線のレイアウトに悩む時間が大半だった。 とりあえず電源を入れたら BIOS 設定(今は UEFI というらしいが)画面に辿り着いて、各種デバイスもキチンと接続できているであろうことを確認する。ここで memtest86+ を実行放置させながら寝る。

翌朝、memtest が二周ちょっと Pass しており、Error が 0 である事が確認できた。ここで Windows10 Home 64bit をインストールする。OS を ssd にインストールするのは初めてだ。速い、HDD よりも格段に速い。

次に行うべきは、マザーボード付属の各種ドライバ・ユーティリティのインストール。これを行わないと、オンボードのネットワークすら使えないようだ。

Asus の AI Suite 3 ユーティリティについて、Ez Update というアプリケーションを更新すると、ログイン直後エラーが発生するようになった。Asus のサイトからこのユーティリティをダウンロードしてインストールすれば、このエラーが解消されるようになった。

Sandisk のサイトから ssddashboard をダウンロードしてインストールする。 480GB ssd の接続速度が 3Gb/s になっている、との報告あり。SATA ケーブルをよく見ずに接続していたせいか。

自分で作成した文書やソースコード等は、NAS に置くようにしていたので、せいぜい使用履歴からファイルを開くことが出来ない程度の不便さで済むから助かる。

ドライバ

繋ぐだけでok

  • Roland Quadcapture (但し最初は失敗する。そのまま再起動すると認識してた。)
  • Razer DeathAdder 2013
  • Razer BlackWidow Chroma
  • Hori RAP5 Silent

ダウンロード&インストール

  • Epson PX-7V(ネットワーク接続)
  • Logicool G430
  • Logicool G27(G430 とは共通性のないユーティリティを使う)

アプリケーション

とりあえず使っていたアプリケーションは移行させないと、色々滞る。

  • Chrome
  • Firefox
  • Thunder Bird (旧プロファイルをコピーすればそのまま使える。gmail は二段階認証の再登録が必要)
  • Google日本語入力
  • Photoshop Lightroom 4
  • Battle.net client と関連ゲーム
  • Steamと関連ゲーム
  • Origin と関連ゲーム
  • Adobe Production Premium 5
  • QNAP qfinder
  • Cygwin 64bit
  • gpg4win
  • Finale 2012
  • Finale 2014
  • JamVOX

導入予定のアプリケーション

  • Libre Office
  • Emacs
  • Git
  • Visual Studio
  • Unreal Engine 4
  • Sonar X3

ベンチマーク (定格 Intel HD Graphics 530)

3D Mark - Time Spy

Graphics score
268
CPU test
4482
Over all
312

ベンチマーク (定格 MSI GeForce 1080 Armor 8G OC)

3D Mark - Time Spy

Graphics score
7256
CPU test
4991
Over all
6793

3D Mark - Fire Strike

Graphics score
22862
Physics score
12621
Combined score
7792
Over all
17384

2016年4月7日木曜日

smart pointer with C API

C のライブラリを C++ で使う際、終了処理が必要なオブジェクトへのポインタを C++ のスマートポインタとして扱う場合、どの様に破棄させるのが良いのだろうか、という話。

C++ 向けに設計されたのオブジェクトというのは、大抵はデストラクタが定義されており、単に delete を呼び出す事で終了処理が行う設計になっているので、ポインタ経由で扱っても delete するだけで良い。 これに対して C 用に書かれた API は、コンストラクト関数があるオブジェクトのポインタを返し、それを使い終わると、デストラクト関数にそのポインタを渡す、といったスタイルが多い。 例えば ANSI C ライブラリでファイルを操作するときには、fopen 関数で FILE 型のオブジェクトへのポインタが返ってくるので、それを介して操作をし、使い終わったらそのポインタを fclose 関数に渡してオブジェクトの後片付けを行うといったスタイルである。 実例を挙げるまでも無いかも知れないが、以下の様なパターン。

  FILE* fp = fopen("file_a.txt", "r");
  ...
  fclose(fp);

この様な API を C++ で使う場合、直接ポインタを扱う事も当然可能ではあるものの、折角便利なスマートポインタが用意されているのだから、それを使いたくなるのは自然だし、寧ろそうすべきだ。 unique_ptr, shared_ptr のデフォルト deletor は、渡しておいたポインタに対して delete を行うだけで、C の関数では、new を使った初期化が行われる事を期待できないから、その様な恐らく malloc されたであろうヒープメモリを delete してしまうことになる。これは規格上未定義の動作を招く事になる。 勿論これらスマートポインタには、廃棄時のカスタマイズという場面も想定されており、deletor を指定することが出来るようになっている。 とはいえ、やり方をが幾つか思いつくので、どういうものを渡すのが良いのだろうかと迷った。この様な場合での場面での手法を幾つか挙げ、考えてみる。

// 検証に先立って、この様な wrapper を用意しておいて、deletor が呼ばれたことを確認できるようにしておく。
void wrap_fclose(FILE* fp) {
  cout << "called wrap_fclose" << endl;
  if (fp) fclose(fp);
}

ファンクタを実装する場合

unique_ptr のリファレンスを見て最初に頭に浮かんだ方法が、私の場合はこれだった。 最も素直な方法だと思う。安全確実だが、オブジェクトの型毎に定義が必要になるのが面倒だ。

// FILE* を受け取るファンクタを定義する。
struct FCloser {
  void operator()(FILE* fp) {
    wrap_fclose(fp);
  }
};

  // 使うとき
  auto fp = unique_ptr<FILE, FCloser>{
    fopen("data/input.txt", "r"),
  };

構築時の記述はすっきりしてて好ましい。 だがやはり、仮引数の型と呼び出す関数が違うだけの、似たようなコードをいくつも実装するのは面倒だから、これはあとでクラステンプレートを考えよう。

std::default_delete を使う場合

スマートポインタの構築時に、deletor が指定されなかった場合、unique_ptr のデストラクタの中で、std::default_delete<> が呼び出される。 だから、この std::default_delete に対して、テンプレートの特殊化を利用すると、使っている型に対応した deletor が呼ばれるという寸法。

// std::default_delete<> を使う方法。
namespace std {
template <>
struct default_delete<FILE> {
  void operator()(FILE* fp) const {
    wrap_fclose(fp);
  }
};
}

  // 使うとき
  auto fp = unique_ptr<FILE>{
    fopen("data/input.txt", "r")
  };

なるほど、その手が有ったか的な興味深い方法で、私もそういう手法は好む方だが、 結局特殊化の部分でファンクタを丸ごと実装する必要があるので、最初の方法と労力は変わってない。

しかも、Stackoverflow の記事の議論を見ていると、この方法はやめておいたほうが良さそうだ。 思ったように動作はするものの、一旦定義したら最早一切他の振る舞いをさせることが出来なくなる。 また、複数箇所でこの特殊化定義をしてしまった場合、リンカがエラーを出すだろう。 複数人で開発する場合に、default_delete の特殊化が行われている事を確実に周知できるだろうか? 等の懸念が残る。少なくとも積極的に使うべき理由は見当たらない。

std::function を使う場合

unique_ptr の第二テンプレート引数に std::function を指定する。

// Deletor 
  auto fp = unique_ptr<FILE, function<void(FILE*)>>{
    fopen("data/input.txt", "r"),
    &wrap_fclose
  };

テンプレートの第二引数のデフォルト型が std::default_delete なので、適切な型を指定しないとコンパイルエラーになる。悪くないが、型の指定と関数ポインタが分離しているのを纏められないだろうか。

λ 式を使う場合

最も手軽に使える手法だが、複数箇所で使うならどこかで λ 式を変数に代入しておく事になるだろう。

// lambda
  auto fp = unique_ptr<FILE, function<void(FILE*)>>{
    fopen("data/input.txt", "r"),
    [](FILE* p){ wrap_fclose(p); }
  };

テンプレート実引数に std::function を渡して、コンストラクタの第二引数に λ 式を渡す事になる。この例の様な使い方なら、λ 式の代わりに関数ポインタを渡すだけでエエやんか、となる。 関数を呼び出すだけの、この様な場面だとコードが増えるだけに思える。だが、一回しか使わないなら最良の方法かなと思う。

decltype(失敗例)

ちょっと試してみようと思ったが失敗だった手法。 テンプレート第二引数に、関数ポインタで初期化した std::function の decltype を渡してみる。 その結果、コンパイルは出来るが、bad_function_call が throw される。勿論これでは使えない。 std::function はあくまで関数ポインタの型を表しているのでであって、関数ポインタそのものの型を表してくれるわけではないようだ。

// この方法は使えない
  auto fp = unique_ptr<FILE, decltype(function<void(FILE*)>{&wrap_fclose})>{
    fopen("data/input.txt", "r")
  };

コンストラクタの第二引数に関数ポインタの実体を渡せば、当然ながら動作する。

ファンクタを template で実装しておく

以上がググッたり思いついたりしたものだが、やはり最初の方法を template にしておくのが良さそうに思う。 関数ポインタを呼び出すだけだが、どうせなら戻り値も仮引数も、template にしておいたら、別の用途にも流用出来るかもしれない。 ということで、書いたのが以下のクラステンプレート。

// 任意の型の関数ポインタを表わせるファンクタクラステンプレート
template <typename Signature>
struct FuncType;

template <typename ResultType, typename ...Types>
struct FuncType<ResultType(Types ...)> {
  using pointer_type = ResultType (*)(Types ...);

  template <pointer_type Func>
  struct Pointer
  {
    constexpr Pointer() noexcept
    {
    }
    constexpr ResultType operator()(Types ...args) const
      noexcept(noexcept(Func(args...)))
    {
      return Func(args...);
    }
  };
  
  template <pointer_type Func>
  constexpr static auto InstanceOf = Pointer<Func>{};

  FuncType() = delete;
};

任意の関数ポインタ型をテンプレート引数に渡す様にしたい。 戻り値は常に一つでよいが、引数リストは可変長としておく事になる。可変長表現そのものは C++11 から扱えるので問題ない。

さて、ここで肝心の関数ポインタを非型パラメタとして渡さなくてはならないが、可変長テンプレートの後ろにはもう引数を定義出来ない。これを解決する為には、このクラステンプレート内のコンテキストで、テンプレート型を参照した非型パラメタをテンプレート引数とするファンクタを定義すれば良さそうだ。それが上の内部クラステンプレート定義。 なお、std::function の関数的なシンタクスの型表現を真似る為に、定義自体は空で、テンプレートを特殊化した所に全ての定義を記述した。実装初期にこの内部クラスは std::function を継承するようにしていたが、そうするとコンストラクタに constexpr を指定できなかったので取りやめた。

一旦上のクラステンプレートを定義しておけば、任意の型のデストラクト関数を、テンプレートパラメタに渡すだけでよくなる。

// 関数の型と関数ポインタをテンプレート第二引数に渡す
  auto fp = unique_ptr<FILE, FType::Pointer<wrap_fclose>>{
    fopen("data/input.txt", "r")
  };

下にある InstanceOf という変数テンプレートは、shared_ptr 構築時に渡すファンクタのインスタンスとして使用する(unique_ptr に使ってもいいけど)。 一時オブジェクトを作るのは一回だけで良いかなと思う。尤も、shared_ptr には第二引数に関数ポインタを渡すだけで良いので、もうちょっとメタプログラミング真っ只中の文脈でもないと、本末転倒になってしまっている。

// shared_ptr での利用
  auto fp = shared_ptr<FILE>{
    fopen("data/input.txt", "r"),
    FuncType<void(FILE*)>::InstanceOf<wrap_fclose>
  };

構築時の記法も、個人的にはそれなりに気に入っている。ただ、shared_ptr の方には、コンストラクタに関数オブジェクトを渡す方法しか用意されてないので、直交性がない点が気になる。unique_ptr の方も実装を見てみると、内部的には、デストラクト関数をメンバに持っていて、テンプレート型として、関数ポインタを渡しても、結局はファンクタを生成しているようだ。shared_ptr のインタフェイスの方が優れていると思うし、将来標準ライブラリが改良されるとしたら、この方法が deprecated とされるかも知れないな、と思うのは考えすぎかも知れないが、少し心配ではある。

なお、確認に使ったコンパイラは、clang3.7, g++5.2.1 のみ。-std=C++11 以降のオプションが必要。 変数テンプレートの方も利用するならば C++14 以降のオプションが必要。

2016年3月21日月曜日

変数テンプレートでコンパイラのバグに遭遇した

変数テンプレートを使った時、コンパイラのバグに遭遇したので記載する。エラーに遭遇した時のコードとは別のコードを書き下ろしている。


// 変数テンプレートを持つクラス
struct Foo {
  template <typename Tp>
  static constexpr Tp var{0};
};

// それを持つクラステンプレートを定義し、
template <typename VTp, typename T>
struct Bar {
  static constexpr auto Var = VTp::template var<T>; 
};

int main(int argc __attribute__((unused)),
         char *argv[] __attribute__((unused))) {
  // インスタンス化する
  int x = Bar<Foo, int>::Var;
  return 0;
}

これを g++ でコンパイルしてみた。(行番号は上のコードとは対応してません)


templ_var.cc: In instantiation of ‘constexpr const auto Bar<Foo, int>::Var’:
templ_var.cc:10:25:   required from ‘struct Bar<Foo, int>’
templ_var.cc:15:24:   required from here
templ_var.cc:10:25: error: ‘template<class Tp> constexpr const Tp Foo::var<Tp>’ is not a function template
   static constexpr auto Var = VTp::template var<T>; 
                         ^
templ_var.cc:10:25: error: ‘var<T>’ is not a member of ‘Foo’

文法的には正しいハズなんだがなと思いつつも g++ はエラーを報告してくる。それにしても "is not a function template" って云うてるけどこれ関数ちゃうし、と思って、clang 3.7 で試してみるとコンパイルは通って、想定した結果が得られた。 Stackoverflow でも同様の話題が投稿されており、このスレッドのコメントで g++, clang プロジェクト宛のバグレポートもあげられているようだ。 clang では最近修正されたが、g++5.0.2 の現時点では修正される気配は無い模様。

g++ でも動くようにするには、変数テンプレートを使う方で、内部クラスを作ってその中で使えばエラーは回避できるようだ。詳しくは上のスレッドに投稿されているレスで。

2016年3月16日水曜日

emacs から pre タグ内へ paste

blog にコードを貼り付けるとき、別のファイルからテキストをコピーして pre タグ内へ貼り付けている。html や xml ファイルをまるごと自分で書く場合には、<![CDATA[....]]> でくくれば良いが、少なくともここの blog のフォームからの投稿時にはそれが使えないようだった。

C++ の template のように、< や > を使いまくるテキストを投稿するためには、いちいち実体参照表記に変換してやる必要がある。テキストをフォームに貼り付けたら、この変換をやってくれる親切なページがあるが、それでもその操作が段々億劫になってくる。

幸いにも私が使うテキストエディタは emacs なので、テキストを選択してコマンドを実行したら、クリップボードに実体参照変換済みのテキストが乗っている、というようなコマンドは作れそうだ。


(defun copy-html-pre-tag-escaped (&optional st en)
  (interactive "r")
  "Copy text in the region. and replace meta characters for html pre tag."
  (kill-new
   (replace-regexp-in-string
    "<" "&lt;"
    (replace-regexp-in-string
     ">" "&gt;"
     (replace-regexp-in-string
      "&" "&amp;"
      (buffer-substring-no-properties st en)))))
  )

文字列置換は別に正規表現で無くていいと思ったが、それらしい関数が探しきれなかった。 使い方はリージョンを選択してこのコマンドを呼ぶ(M-x copy-html-pre-tag-escaped)だけ。 実体参照変換後の文字列がクリップボードに乗っているので、それを paste すれば良い。

一応説明を書いておくと、buffer-substring-no-properties でリージョンにあるテキストを取得して、三種類の文字を対象に文字列変換する。 それを kill-new という物騒な名前の関数に渡せば kill-ring に入ると同時にクリップボードにもコピーされているという仕組み。

ググっている内に複数文字列を一挙に変換する関数をパッケージとして公開しているページを見つけた。面白そうなページなので、後でじっくり読んでみよう。

2016年3月10日木曜日

include guard 用文字列を uuid にして elisp で自動挿入

OSDN の wordpress にお世話になってたが、highlight.js の使い方がわからず、こっちに移行しようと思う。 highlight のテストがてら、あちらでの投稿をこちらに再投稿する。

boost のヘッダファイルを見ていると、include guard 用のシンボルが UUID_xxxxx... という感じにしてあるのに気づいた。 なるほど。これは確かに uuid を使った方が #define [ファイル名] なんかよりも合理的に思える。 これは真似したいなと思うが、ヘッダファイルを新規作成する度に、uuidgen した文字列を sed s/-/_/g してコピーしてソースに貼り付け、とか考えると明らかに面倒だ。 じゃあ、emacs lisp で自動化してやろうか、と思って書いたのが以下の lisp コード。

boost のヘッダファイルを見ていると、include guard 用のシンボルが UUID_xxxxx... という感じにしてあるのに気づいた。 なるほど。これは確かに uuid を使った方が #define [ファイル名] なんかよりも合理的に思える。 これは真似したいなと思うが、ヘッダファイルを新規作成する度に、uuidgen した文字列を sed s/-/_/g してコピーしてソースに貼り付け、とか考えると明らかに面倒だ。 じゃあ、emacs lisp で自動化してやろうか、と思って書いたのが以下の lisp コード。


(defun insert-random-uuid ()
 (interactive)
 "Inserts a UUID string suitable to CPP identifier"
 (shell-command "echo UUID_$(uuidgen | tr a-z\- A-Z\_)" t))

(defun insert-include-guard ()
 (interactive)
 "Inserts C/C++ header include guard."
 (progn
  (insert "#if !defined(")
  (insert-random-uuid)
  (let (p1 p2)
   (setq p1 (point))
   (end-of-line)
   (setq p2 (point))
   (kill-ring-save p1 p2))
  (insert ")\n#define ")
  (yank)
  (insert "\n#endif /* ")
  (yank)
  (insert " */")
  (beginning-of-line)
  (open-line 3)
  (forward-line)
  ))

これが eval された後、emacs で M-x insert-include-guard すると、


#if !defined(UUID_364889E3_11A2_493E_B861_5C20F63968D8)
#define UUID_364889E3_11A2_493E_B861_5C20F63968D8

#endif /* UUID_364889E3_11A2_493E_B861_5C20F63968D8 */

というような感じのテキストが自動挿入される。