標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

std::vector / std::set 両方からデータを取れる関数をあまりテンプレートにしたくない

というような主旨のことを述べたら、boost::any_range をオススメされました。

が、弊社では外部ライブラリの導入がめんどくさいので、boost といえど、あまり使いたくない(未だに Visual Studio 2010 がメインに使われてるようなところだからね)

とすると、コールバック関数かなぁ

#include <iostream>
#include <vector>
#include <functional>
#include <set>

void put(std::function<bool(std::string&)> each)
{
    std::string value;
    while( each(value) ){
        std::cout << value << std::endl;
    }
}

int main()
{
    std::vector<std::string> v;
    v.push_back( "a" );
    v.push_back( "b" );
    v.push_back( "c" );

    auto p=v.begin();
    put( [&v,&p](std::string &value){ if( p == v.end() ){ return false; } value = *p ; p++ ; return true; } );

    std::set<std::string> s;
    s.insert( "a" );
    s.insert( "b" );
    s.insert( "c" );

    auto q=s.begin();
    put( [&s,&q](std::string &value){ if( q == s.end() ){ return false; } value = *q ; q++ ; return true; } );
}
$ gcc -std=c++14 it.cpp -lstdc++
$ a
a
b
c
a
b
c

うーん、呼び出しがすごく面倒くさい。

標準ライブラリに std::vector , std::set の既定クラス的な std::collection とか std::enumerable とかあればよかったのに。

本件、たぶん、続きます。

ExcelVBA のソースを .xls ファイルから抽出する JScript を書いた

Excel VBA の保守をしなければいけないんだけども…

  • そのアプリは 開いただけで自動的に VBA が自動的に起動してしまう
  • xlt ファイルなので、名前を変えないまま保存できず、毎回自分自身を選択しなくてはいけない

こういう場合、Ariawase という Windows Scripting Host のツールで VBA のソースをエクスポート / インポートするのが定石です。が、ちょっと User Interface が好みではない。

ということで、同ツールのソースから必要最小限の部分をパクって 参考にして 、エクスポートするコードを JScript で書いてみました (本当は Go でいきなり書きたかったけど、COM の構成を理解するために、まず JScript でプロトタイプを書いた)

exportvba.js

var args = WScript.Arguments;

if( args.length < 1 ){
    WScript.Echo( "cscript exportvbs.js XLSNAME" );
    WScript.Quit();
}
var fsObj = new ActiveXObject("Scripting.FileSystemObject");
var excel = new ActiveXObject("Excel.Application");
try{
    excel.Visible = true;
    var targetPath = fsObj.GetAbsolutePathName(args(0));
    WScript.Echo( "extract: " + targetPath );
    var book = excel.WorkBooks.Open(targetPath);
    for( var p = new Enumerator(book.VBProject.VBComponents) ; ! p.atEnd() ; p.moveNext() ){
        var obj = p.item();
        var name = obj.Name;
        switch( obj.Type ){
            case 1: name += ".bas"; break;
            case 2: name += ".cls"; break;
            case 3: name += ".frm"; break;
            case 100: name += ".dcm";break;
        }
        var fullpath = fsObj.GetAbsolutePathName( name );
        WScript.Echo( fullpath );
        obj.Export( fullpath );
    }
}finally{
    if ( excel != null ){
        excel.Quit();
    }
}

cscript exportvba.js foo.xls とすると、カレントディレクトリに foo.xls 内の VBA ソースを展開するという本当にシンプルな動作です。

さて、次のステップは

  • インポート版を作る
  • Go 言語で作り直す

かな。これ、いつになったら出来るかな!?

nyagos 4.4.5_0 を公開しました。

4.4.4_4 は欠番になりました。今回の修正は、パッチリリースというには、少し修正が多めになりましたので。

今回の修正は、~/.nyagos の簡素化がメインです。

  • Lua関数: nyagos.dirname() を実装

これは UNIX の dirname 相当を行うための関数です。

$ lua_e "print(nyagos.dirname('C:/User/hymko/Share'))"
C:\User\hymko

みたいに使います。実は nyagos.dirname('A')nyagos.pathjoin('A','..') と等価なのは秘密です。

  • C-o で複数ファイル選択をサポート(Space,BackSpace,Shift-H/J/K/L,Ctrl-Left/Right/Down/Up)

ごらんのとおり、複数ファイル選択ができるようになりました。

  • Alt-Y(引用符つきペースト)で、改行前後に引用符を置くようにした

これは git status でリストされた修正ファイルをマウス操作でコマンドラインにペーストする時、空白が含まれたファイル名も扱えるようにするための修正です。 (Visual Studio だと、My Project なんていう名前のフォルダーがあって、これが難儀なんですよね)

  • C-o で表示される選択肢がディレクトリの時、末尾に \ (Linux では /) をつけるようにした。

これで選択後に TAB を1回打鍵するのが省けます。

  • nyagos.envadd("ENVNAME","DIR") と nyagos.envdel("ENVNAME","PATTERN") を実装

.nyagos での%PATH% の追加/削除の支援関数です。こんな感じで使います。

nyagos.envdel("PATH",
    "Oracle","Lenovo","Skype",
    "chocolatey","TypeScript","WindowsApps",
    "Wbem","dotnet")

これは %PATH% から OracleLenovo といったキーワードを含むフォルダーを削除します。長過ぎる %PATH% は誤動作を引き起こすこともあるので、いつも Lua 関数を作って短くしていたのですが、いい加減定番コードなので本体にツール関数として内蔵させた方がよいのではないかということで実装してみました。

nyagos.envadd("PATH",
    "C:\\go\\bin",
    "C:\\TDM-GCC-64\\bin",
    "%ProgramFiles%\\Git\\bin",
    "%ProgramFiles%\\Git\\cmd",
    "%ProgramFiles%\\Git\\cmd",
    "%ProgramFiles%\\Git\\usr\\bin",
    "%ProgramFiles(x86)%\\Git\\bin",
    "%ProgramFiles(x86)%\\Git\\cmd",
    "%ProgramFiles(x86)%\\Git\\usr\\bin",
    "%ProgramFiles%\\Subversion\\bin",
    "%ProgramFiles(x86)%\\Subversion\\bin",
    "~\\Share\\Program Files\\idmanager",
    "%VBOX_MSI_INSTALL_PATH%",
    "~\\Share\\bin",
    "~\\Share\\cmds")

こちらは逆に %PATH% にフォルダーを追加します。こちらは少々機能追加してありまして、%~% というワードを環境変数、~\ をホームディレクトリ名に展開するといったことを行います。また、ディレクトリとして存在しない場合は追加を行いませんので、.nyagos を DropBox などで共有化して、各PCでありえるケースをとにかく書くといったことも可能になります。

  • nyagos.pathjoin() で %ENVNAME% と ~\,~/ を展開するようにした

これは文字通りです。いちいち nyagos.env.ENVNAME とかいちいち書くの面倒なので。

以上になります。

strings.EqualFold はどの程度の速度か

strings.EqualFold について言及がなかったので、測ってみました。

追加行:

func isDevNull4(name string) bool {
    return strings.EqualFold(name,"nul")
}
func BenchmarkS4(b *testing.B) {
    test(b, isDevNull4)
}

結果:

$ go test -bench .
goos: windows
goarch: amd64
BenchmarkS1-4           50000000                29.5 ns/op
BenchmarkS2-4           50000000                29.1 ns/op
BenchmarkS3-4             200000              9844 ns/op
BenchmarkS4-4           20000000               101 ns/op

自分的には「なんか、まぁまぁ速かったけど、速すぎるので、何かミスってる可能性大」と思っていたんですが、「そんなもんだと思います」とのコメントをいただきました。

まぁ、考えてみれば、S3 で使われている strings.ToLower(name) == "nul" は文字列の領域確保とインスタンス作成が入っているため、この結果は当然かもしれません。

mattn さん、どうもありがとうございました。

そのソリューションを開くべき Visual Studio のバージョンは?

ソリューションファイル(*.sln)の記述

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "HOGEHOGE", "HOGEHOGE\HOGEHOGE.vbproj", "{E951CFF8-148E-40D5-93CD-17118375A437}"

コメント行に Visual Studio 2010 というテキストがあるので参考になる。だが、これは、このソリューションファイルを作成した時の Visual Studio のバージョンであるので、現在使うべき Visual Studio であるという保証はない。(新しいバージョンの Visual Studio 向けにアップグレードしても、このコメント欄は更新されない)

プロジェクトファイル(*.vbproj等)の記述

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

古い Visual Studio は、新しい ToolsVersion をサポートしていない場合があるため、これで使用すべき Visual Studio は制限される。

MSBuild ツールセット (ToolsVersion) - Visual Studio | Microsoft Docsによると

if you open a Visual Studio 2008 project in Visual Studio 2010, the project file is updated to include ToolsVersion="4.0". If you then try to open that project in Visual Studio 2008, it doesn't recognize the upgraded ToolsVersion and therefore builds the project as though the attribute was still set to 3.5.

Visual Studio 2010 and Visual Studio 2012 use a ToolsVersion of 4.0. Visual Studio 2013 uses a ToolsVersion of 12.0. Visual Studio 2015 uses ToolsVersion 14.0, and Visual Studio 2017 uses ToolsVersion 15.0.

つまり

ということになるようだ。

(追記)ソリューションファイル

ソリューションファイルには、さらに次のような文もあることが確認された。奥が深い

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.40629.0
MinimumVisualStudioVersion = 10.0.40219.1

VMware Workstation 12 のイメージがディスクを圧迫してきたので整理した

どうも VMware はバージョンやエディションによって、インターフェースがだいぶと違うようで、ググっても違う製品向けのものが出てきてしまう。

試行錯誤の結果、下記の手順でディスク消費を減らした。
(10個ちかく VM があったのだが、500GB くらい浮いた)

  • 不要なスナップショットを削除する
    • ただし、この時点ではディスクは開放されない(ように見える)
  • VM(M)」→「管理(M)」→「ディスクのクリーンアップ」
    • こちらは確実にディスクが解放される(ようだ)
    • この機能の場所が分からなくて、今まで放置していたのは秘密だ。
  • 使用していない VM の削除
    • 「ファイル(F)」→ 「OVF へのエクスポート(E)」で、OVF フォーマットでエクスポートする
    • VM(M)」→「管理(M)」→「ディスクから削除(D)」
    • エクスポートした OVF ファイル一式は DropBox のフォルダーにおいて、スマートシンクで「オンラインのみ」にすることで、ローカルディスクの消費容量をゼロにする(会社の DropBox はアホほど容量が余っているのだ)
    • 削除した VM がクローンの場合、クローン元の VM のクローン時のバックアップも削除してよくなる(はず)

なお、今回はリストアのテストまでしていないので、もしかしたらインポートできないという事態もありえないともいえない。真似る時は OWN YOUR RISK で頼む

(備考)リンククローンについて

  • リンククローンして作った VM は、仮想マシンの詳細欄に「クローン: クローン元のイメージのパス 」が表示される
    • このVMでは、イメージファイルのフルコピーはバックアップにならない(リストア時にクローン元のVMも必要になる)。OVFファイルにエクスポートした方がよい
  • リンククローンを過去作ったことがある VM は、スナップショットマネージャで見た時に、リンククローンした時のスナップショットが自動作成される。これを削除しようとすると警告される。
    • クローン先のイメージを完全削除しないとリンク時のスナップショットを消すわけにはいかない

以上

*.vcxproj/vbproj/csproj のパーサーを書いているが、$(VCTargetsPath)の導出元がわからない

Visual Studio のプロジェクトで生成されるプロダクト(実行ファイル・DLLファイル)のパスを生成する機能が vf1s.exe に必要なので、"encoding/xml".Unmarshal関数 で読み取っていました。

Unmarshal 関数は、タグに合わせてフィールドを用意した構造体に解析結果を放り込むというもので、解釈する XML タグの種類がそれほど多くない場合などには非常にお手軽で便利なんですが、プロジェクトファイルのように「任意の Condition 属性の中にある条件式が真の時だけタグの中身を有効にする」というif 文的な処理はさすがに厳しいので、こちらの記事を参考にして、タグの開始時・終了時・その他の部分をトークン化して読み出すDecoder関数を使うようにしました。 (一応、GoDoc さらしときます→ https://godoc.org/github.com/zetamatta/vf1s/projs

これで、手元にあるプロジェクトファイルでは概ねうまくゆくようになったんですが、まだ一部ちゃんと解釈できていないところがありまして… $(VCTargetsPath) などデフォルトで設定されている変数の値なんですね。これがわからないと <Import> タグで取り込むファイルのフルパスも導出できないんです。

Visual Studio 2019 Community Edition の $(VCTargetsPath) に限って言うと、C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\ になるみたいなんですが、あらゆるバージョンの Visual Studio での普遍的な値を得るための方法が不明。ググってみてはいるんですが

  • Visual Studio というよりは MSBuild がこれらの変数を定義している
  • レジストリに定義があるようだが、これがとんでトラブルになるケースが多い

ということくらいしかわかりませぬ。これさえ分かれば、他の変数もインポート先の XML 経由で得られるんですが、ぐぬぬ