C++幼女先輩

プログラミング成分多め

会社のホームページ変更とブログ移転の検討

www.murasame-lab.com
上記が今の会社のホームページとなっている
知人のWebデザイナーが仕事が暇というので仕事をしてもらったが、その後体調を崩してメンテがしてもらえていない
そのため、会社のホームページを一新しようと思っている

とりあえず、2年ほど前から海外移住した友達のWebデザイナーに打診中だが
断られたら、ひとまずWordPressで作ろうと思う(実は今のページを作る前にモックでWPで作ったのがある)

それに加え、自社ドメインを強くするためにも、ブログを自ドメインにしようとおもう
はてなブログはProにすれば独自ドメインにすることが可能だが
もしかしたら自社のWordPressに変更するかもしれない

移行手順はもう完璧だ

とりあえず、少しずつ会社らしくしていこう

Go言語にデストラクタがないので何とかしたい

Goの真実

Goにはコンストラクタもデストラクタもない
ので、便宜上 コンストラクタに当たる関数は 構造体名の頭にNewのプリフィクスを入れた関数である
C言語だと init_hoge とするのが NewHogeになっただけだ
そして、そのNew関数は構造体のポインタを返す(Cでも呼び方わすれたが構造体をClassっぽく書くときに同じことをする)
結局C言語なのだ(念のため、C++ではない)

そしてデストラクタがないので、呼び出し側が終了関数を明示的に呼ばなければならない
Cだと delete_hoge とか release_hoge、teardown_hoge・・・ 呼び名が統一されてる覚えがない
Goにもデストラクタとして呼び名が統一されている気がしない
つまり、よく呼び忘れてメモリリークする!!

https://play.golang.org/p/i6-vTaTVx9Z

package main
import (
    "fmt"
)


type File struct{
  filename string
}

func NewFile(f string) *File{
  fmt.Printf("File{%s} new&open\n", f)    
  return &File{f}
}

func(s *File)Read(){
  fmt.Printf("File{%s} read\n", s.filename)    
}

func(s *File)Close(){
  fmt.Printf("File{%s} close\n", s.filename)    
}




func main() {
  fmt.Println("main start")    

  file := NewFile("test")
  file.Read()

  fmt.Println("main finish")    

}



main start
File{test} new&open
File{test} read
main finish

上記のコードはRAII原則にのっとり、New時にリソースをOpenしているが
残念なことにライブラリを使う人がClose()を呼び忘れたため、リソースリークしている

defer

いちおうGoにはdeferがあり、関数が終了した時に実行されるファイナライザを書くことができる

https://play.golang.org/p/LPEm7oDSf2M

...
  file := NewFile("test")
  defer file.Close()
  file.Read()
...

main start
File{test} new&open
File{test} read
main finish
File{test} close

これは便利だが、このdeferはライブラリを使う側の人間が忘れたらCloseしてくれない

構造体の方でdeferを入れたい

そう考えるかもしれないが、deferはあくまで、関数の終了時のファイナライザだ
コンストラクタにdeferを入れると当然コンストラクトした後にすぐCloseが走る

https://play.golang.org/p/aav2psenFxF

...
func NewFile(f string) *File{
  // file Open処理をする
  fmt.Printf("File{%s} new&open\n", f)    
  file := File{f}
  defer file.Close()
  return &file
}
...

main start
File{test} new&open
File{test} close
File{test} read
main finish

runtime.SetFinalizer

見るからに悪手だが、ランタイムライブラリに、ガーベージコレクションが走った時に実行されるコールバックがある

  x:= "hoge"
  runtime.SetFinalizer(&x, func(x *string){fmt.Println("SetFinalizer")})

  // 上記だと即終了しGCが走らないので故意にGCを走らせ待機させる

と、上記では xがGCされる時に 次のラムダ式が呼ばれる。ラムダ式の引数はGCされるオブジェクトのポインタだ

https://play.golang.org/p/QYkVhC1uQKP

これを構造体に当てはめられないか?

コンストラクタでSetFinalizerする

コンストラクタで構造体を作成し、ポインタを使う側に返すが、このポインタにSetFinalizerを設定し
構造体がGCされるときにCloseしてみよう
念のためインスタンスを複数作る

https://play.golang.org/p/LL3ejYYcowe

package main
import (
    "fmt"
    "runtime"
    "time"
)


type File struct{
  filename string
}

func NewFile(f string) *File{
  // file Open処理をする
  fmt.Printf("File{%s} new&open\n", f)    
  file := File{f}
  runtime.SetFinalizer(&file, func(f *File){f.Close()})

  return &file
}

func(s *File)Read(){
  fmt.Printf("File{%s} read\n", s.filename)    
}

func(s *File)Close(){
  fmt.Printf("File{%s} close\n", s.filename)    
}

func(s *File)Nop(){
  fmt.Printf("File{%s} nop\n", s.filename)    
}


func hoge(){
  file := NewFile("test")
  file.Read()
  file2 := NewFile("test2")
  file2.Read()
  file3 := NewFile("test3")
  file3.Nop()
}

func main() {
  fmt.Println("main start")    
  
  hoge()
  runtime.GC()
  time.Sleep(1 * time.Second)

  fmt.Println("main finish")     
}

main start
File{test} new&open
File{test} read
File{test2} new&open
File{test2} read
File{test3} new&open
File{test3} nop
File{test3} close
File{test2} close
File{test} close
main finish

綺麗に動いてしまった・・・

レシーバーにSetFinalizerする

先ほどはコンストラクタにSetFinalizerをしたので、たぶん確実にFinalizerが走るが
特定メソッドで、レシーバーに対してSetFinalizerしたらどうなるか?
たとえばReadメソッドでやってみる

func(s *File)Read(){
  runtime.SetFinalizer(s, func(f *File){f.Close()})
  fmt.Printf("File{%s} read\n", s.filename)    
}


main start
File{test} new&open
File{test} read
File{test2} new&open
File{test2} read
File{test3} new&open
File{test3} nop
File{test2} close
File{test} close
main finish

すばらしい、意図したとおりに動いた(file3はReadメソッドを読んでないのでFinalizeされてない) 使うシーンがあるか不明だが、例えばOpenメソッドを呼んだ時だけCloseのFinalizerを設定
という使い方も不可能ではなさそうだ

でもね

GCされるまでは開放されないので、もし長時間GCされずに存在していたら
その間ファイルを開きっぱなしだったり、ネットワークリソースを開放しない事になる
だから、SetFinalizerでデストラクターをするのは、よくない事は明確である・・

どこを間違えたかと思ったが、Goはオブジェクト指向言語ではないというのが答えだ
本当は関数型のように書くべきなんだろうか?

Callback にすべきではないか?

コンストラクタでdeferしても、コールバックで続きの処理を行えば
コンストラクタのコンテキスト内なので問題ないはずだ・・・

https://play.golang.org/p/1ZBphVFMHKM

  package main
import (
    "fmt"
    "runtime"
    "time"
)


type File struct{
  filename string
}

func NewFile(f string, cb func(file *File)*File) *File{
  // file Open処理をする
  fmt.Printf("File{%s} new&open\n", f)    
  file := File{f}
  defer file.Close(nil)

  if(cb != nil){
    return cb(&file)
  }    
  return &file
}

func(s *File)Read(cb func(file *File)*File)*File{
  fmt.Printf("File{%s} read\n", s.filename)    
  if(cb != nil){
    return cb(s)
  }    
  return s
}

func(s *File)Close(cb func(file *File)*File)*File{
  fmt.Printf("File{%s} close\n", s.filename)
  if(cb != nil){
    return cb(s)
  }
  return s
}

func(s *File)Nop(cb func(file *File)*File)*File{
  fmt.Printf("File{%s} nop\n", s.filename)    
  if(cb != nil){
    return cb(s)
  }
  return s
}


func hoge(){
  _ = NewFile("test", func(file *File)*File{
    file.Read(func(file *File)*File{
      fmt.Println("callback hell")
      return file          
    })
    return file
  })
}

func main() {
  fmt.Println("main start")    
  
  hoge()
  runtime.GC()
  time.Sleep(1 * time.Second)


  fmt.Println("main finish")    

  
}



main start
File{test} new&open
File{test} read
callback hell
File{test} close
main finish

やったぜ!!(やりたくない

もう少し実用的に

ReadやWriteの引数ちゃんと作って、もう少し実用的にします
エラーも返さないとね(エラー処理は省略

https://play.golang.org/p/XbAWauNUNyZ

package main

import (
    "fmt"
    "runtime"
    "time"
)

type File struct {
    filename string
}

func NewFile(f string, cb func(file *File) (*File, error)) (*File, error) {
    // file Open処理をする
    fmt.Printf("File{%s} new&open\n", f)
    file := File{f}
    defer Close(&file, nil)

    if cb != nil {
        return cb(&file)
    }
    return &file, nil
}

func Read(file *File, len int, cb func(file *File, readed *string) (*File, error)) (*File, error) {
    fmt.Printf("File{%s} read\n", file.filename)
    data := "readed"

    if cb != nil {
        return cb(file, &data)
    }
    return file, nil
}

func Write(file *File, data *string, cb func(file *File, written int) (*File, error)) (*File, error) {
    written := len(*data)
    fmt.Printf("File{%s} write {%s}  size={%d}\n", file.filename, *data, written)

    if cb != nil {
        return cb(file, written)
    }
    return file, nil
}

func Close(file *File, cb func(file *File) (*File, error)) (*File, error) {
    fmt.Printf("File{%s} close\n", file.filename)
    if cb != nil {
        return cb(file)
    }
    return file, nil
}

func Nop(file *File, cb func(file *File) (*File, error)) (*File, error) {
    fmt.Printf("File{%s} nop\n", file.filename)
    if cb != nil {
        return cb(file)
    }
    return file, nil
}

func hoge() {
    _, _ = NewFile("test", func(file *File) (*File, error) {
        Read(file, 5, func(file *File, readed *string) (*File, error) {
            fmt.Printf("READ{%s}\n", *readed)
            data :="hogehoge"
            Write(file, &data, func(file *File, written int) (*File, error) {
                fmt.Printf("WRITE [%d]bytes\n", written)
                return file, nil
            })

            return file, nil
        })
        return file, nil
    })

}

func main() {
    fmt.Println("main start")

    hoge()
    runtime.GC()
    time.Sleep(1 * time.Second)

    fmt.Println("main finish")

}


main start
File{test} new&open
File{test} read
READ{readed}
File{test} write {hogehoge}  size={8}
WRITE [8]bytes
File{test} close
main finish

ライブラリを使う側からはもうCloseの事考えなくてよくなりました
やったね!!

しかし、コールバック地獄、エラー処理の場所・・・もっと考えなければならない事が増えてしまいました

しかもGoはJavaScriptC#のような、クロージャを簡単に書く書式もないので、まいかいfunc ()と大変

コールバック必要なのはNewだけだったんや!

別に非同期プログラミングのようなコールバックする必要はない
GoはGoroutineという素晴らしい仕組みがあるので、同期っぽく書けばいい
わざわざ人間に不親切なコールバック地獄にする必要がない

コールバックを使った理由はNew関数を抜けるときにdeferdでCloseし忘れを防ぎたい
処理が終わるまでNew関数を抜けないためだ
ReadやWriteまでコールバックする必要がない。Newだけでよかったんだ

https://play.golang.org/p/WtixVskDI7R

package main

import (
    "fmt"
)

type File struct {
    filename string
}

func NewFile(f string, cb func(file *File)error) ( error) {
    // file Open処理をする
    fmt.Printf("File{%s} new&open\n", f)
    file := File{f}
    defer file.Close()
        err := cb(&file)

    return err
}

func (s *File) Read(){
    fmt.Printf("File{%s} read\n", s.filename)
}

func (s *File) Close() {
    fmt.Printf("File{%s} close\n", s.filename)
}

func (s *File) Nop() {
    fmt.Printf("File{%s} nop\n", s.filename)
}

func hoge() {
    _ = NewFile("test", func(file *File)error{
            file.Read()
        return nil
    })
}

func main() {
    fmt.Println("main start")

    hoge()

    fmt.Println("main finish")

}

main start
File{test} new&open
File{test} read
File{test} close
main finish

当然ちゃんとクローズされるし、コールバックは初回のNewだけなので、コードの見通しも悪くない
ただ、コールバックとして全てを中にいれなければいけない
オブジェクト指向脳だと自由に構造体を使い回したいと思うだろう

コールバックとオブジェクト指向のハイブリッド

Goにはメソッドのオーバーライドやデフォルト引数がない
可変長引数で処理できるが今回はそこは手抜きで、CallbackがnilだとClose管理はライブラリでは行わない

https://play.golang.org/p/M6g-dqUz86P

package main

import (
    "fmt"
)

type File struct {
    filename string
}

func NewFile(f string, cb func(file *File) error) (*File, error) {
    // file Open処理をする
    fmt.Printf("File{%s} new&open\n", f)
    file := File{f}
    var err error = nil
    if cb != nil {
        defer file.Close()
        err = cb(&file)
    }

    return &file, err
}

func (s *File) Read() {
    fmt.Printf("File{%s} read\n", s.filename)
}

func (s *File) Close() {
    fmt.Printf("File{%s} close\n", s.filename)
}

func (s *File) Nop() {
    fmt.Printf("File{%s} nop\n", s.filename)
}

func hoge() {
    // クロージャで自動的にCloseする
    _,_ = NewFile("test", func(file *File) error {
        file.Read()
        return nil
    })
    
    // クローズは使う側が責任持つ
    file2, _ := NewFile("test2", nil)
    defer file2.Close()
    file2.Read()

    // 忘れると当然リークします
    file3, _ := NewFile("test3", nil)
//  defer file3.Close()
    file3.Read()
}

func main() {
    fmt.Println("main start")

    hoge()

    fmt.Println("main finish")

}


main start
File{test} new&open
File{test} read
File{test} close
File{test2} new&open
File{test2} read
File{test3} new&open
File{test3} read
File{test2} close
main finish

結局どれがいいのか?

Finalizerは動作はするが、GCが走るまで開放が行われないので実用的ではない
コールバック地獄はダメ!絶対!(おそらくGoはGoroutineで同期的に書く設計なので、promise/futureやasync/awaitのようなコールバック解決ライブラリはあまり出ない)
Newのコールバックは場合によっては使えると思う
でも基本は、ライブラリを使う側が責任もって後始末しなければいけないっぽい

ハイブリッドは良さそう

デスクトップPCのOS入れ直し

デスクトップPCを新調した

5月にデスクトップPCを超パワーアップした
CPUを corei9 9700Kにし
メモリを32GB
マザーはSLI対応
ビデオはGeforce1080Tiのまま

ところが1つだけ失敗がある
SSDを交換するの忘れた
ので買う事になった

現在ドライブは
SSD 256GB + HDD 4TBだ
OSが30GB、開発ツールが100GBほど使うと残りは100GB程度
結構厳しい
そのため、現在はゲームは全部HDDドライブに入れている
なんのためのゲーミングPCだ・・

という事で、近いうちに1TB~のSSDを買い、OSを移そうと思っている

アクティベートとか面倒化と思っていたが
どうやらSSD入れ替え程度ではOSの再アクティベートも不要で
イメージソフトを使えばイメージコピーすれば、特にインストールし直す事なく使えそうだ
非常にたすかる

うちのマザーは M.2を3本までさせるらしい
1個しか刺さない予定だけど

これで今まで悩まされていた容量問題が解決する

近況

近況

ブログあんま書いてないならブログなんてやめちまえ!
おっしゃる通り

仕事

最近はUNITYのゲーム作成とGolangでのセキュリティ&暗号化関連
の2本立てでございます

UNITYのゲームの方は、かなり人気なタイトルの続編となっていて、今までに何作も出ていて
とても重みを感じます
メインからまるっと開発まかされているので、責任も重大です!!
作業内容としては、開発のタスク管理から技術からみた仕様へのアドバイス、企画がつかうツール作成
シェーダー、全体のベース作成、高速化、コードレビューなど・・
多岐にわたります
ついでに、他案件のシェーダー書いたりサーバコンサルしたり

Goの方はやっと動いてきた感じです
Goは言語はわかってるけどベストプラクティスがあまり見つけられておらず
あまり好きな言語ではないんですが
人妻の友達のアドバイス
「夫婦が上手くいく秘訣は、相手の事あんま好きじゃなくても毎日嘘でいいから好きっていう事」
だそうなので、毎日Goが好きだという事にしました

ニュースリリース

大きな発表が出来るよう、最終交渉中です。がんばろう

仕事以外

ゲームしたい・・
FPS練習でオーバーウォッチしたい
TPS練習でスプラトゥーン2したい
モンハンワールド Icebone前に進めたい
ストV やりたい

Macbookを使わなくなって1年半がたちました

PC歴

Windows時代

昔は仕事はWindowsだったし、ゲームにツールにWindowsの方が便利だった
言語はC++C#Javaなどなど
サーバはWindowsサーバかLinuxサーバ
だいたいWindowsサーバにはリモートデスクトップLinuxサーバにはTelnetSSH)で接続してたか

2011年。Macbook元年

iPhoneアプリの台頭により、私もMacbookが必要になった
そのため、はじめてのマッキントッシュ、MacbookPro2011を買った
しかもNetBSDベースのOSのため、サーバ開発も1台で出来た
軽いし薄いし、とてもいいマシンで毎日持ち歩いて仕事をした
弱点はゲームが出来ない事ぐらいだが、ゲームは家のデスクトップで。

2016年。WindowsノートPC RazerBlade購入

MBP2011は、初期不良でロジックボードに爆弾をかかえていた
まだ使える状態だが、 後継ノートPCが必要だった
デスクトップではダメな理由は、仕事柄持ち歩く必要があるから
この頃はiPhoneの仕事もあまり来なくなり
UNITYやUnreal、サーバーの案件がメインになっていた
新しいMacbookProはnVidiaチップを積まなくなり、VGAもMBPは弱く、UNITYやUnrealには不利
また、Dockerのおかげでサーバ開発もWindowsで行いやすくなったため
Windows用のゲーミングノートPCを買った
サーバ開発もほぼ問題なく行える
しかも、LinuxMacにくらべ、DockerのファイルI/Oが20倍も速い!!

Goの問題

基本的にDockerを使えばLinuxと変わらずに開発できる
もちろん、Nativeで実行することもだいたい可能である
ところが、Goの開発では当時はPluginとShell実行がWindowsは対応していなかった
そのためMBPが必要になり2017年モデルを購入した

Go問題解消

Goが上記の問題を解消した
そのため、Windowsで問題なく開発出来るようになった
MBP2017はバタフライキーボードが不調ですごくストレスがたまる
しかも、Windowsはその間にDocker技術をすごく発展させていて
今はまだExperimentalだがLCOWで直接ホスト上で動いたり、Windowsコンテナや
VSCodeにWSLが乗ったり・・・
そして、完全にMacbookを使わなくなり1年半がたった

個人的な今のWindowsMacの比較

Windowsの方が良い点

ゲームが出来る
ソフトが多い
スペックのわりに割安
Dockerが速い
GeForceをつめるので機械学習Unreal、Unity開発が良い

Macの方が良い点

iPhone開発を行う事が出来る
iPhoneのTrueDepthやARKitなどのApple独自の技術が使える
海外旅行中に故障してもAppleストアが世界中にある
中古販売価格も高い

gRPCの勉強

gRPCとは

HTTPによるREST APIみたいなものですが、RESTではなく RPCである事など違いがあります
REST APIと比較すると

  • 仕様が柔軟である
  • ヘッダが小さくオーバーヘッドが少ない
  • HTTP2に対応しており、非同期通信が効果的に行える
  • protoファイルを作成し、プロトコルレイヤーを分離できる

などの優れた特徴をもっています
今後gRPCを使った開発が増えてくるんじゃないかな

大切な事はだいたい公式が教えてくれる

https://grpc.io/docs/guides/

公式のガイドに全部書いてあるけど、英語が読めないので・・
私の場合はサンプルコード見るのがはやい!

https://github.com/grpc/grpc-go/tree/master/examples

しかも、サンプル用にプロトコルやシンプルなサーバクライアント
認証用のトーク
など全部用意されているので
コード確認がすぐに出来るすぐれもの!

テストコード

下記にテストコードをかいていく
github.com

Golang再び

Golangの仕事再び
不満点はいっぱいあるけど、今の主流言語のひとつであることは間違いない Golang
仕事の内容は詳しくは書けないが、暗号化とセキュリティが重要な巨大基盤
OpenStackで作ったインフラ上で動かす

前のプロジェクトでGolandを購入したので、Golandを使うことにした
今回はいいデスクトップPCを買ったので、Windowsメインで開発を行うが
1年前とはちがい、今のところGolangWindowsで出来なかった問題が解決した

Goのバージョンアップ

インストールしていたGoが古いので最新に上げた
というのも、下記のGomodulesを使う必要があるので、必須であった
Chocolatey を使えば管理が楽そうだが、今回は普通にインストールをした
単純にインストーラー起動してうわがきすればよい

ちなみに Chocolatey使ったインストールは下記に

qiita.com

go modules

Go11にExperimentalで入った機能のようだ
Go11では機能をONにするために環境変数必要だが、Go12では不要

qiita.com

いままでのGoで不満だった、GOROOT以下にソースファイルを置く必要があったやつ
ライブラリのバージョン依存の解決のために venderディレクトリ使ってほげほげ してた
そういった問題を解決してくれるものだ
設定ファイルに モジュールのパスをエイリアス指定したりできる
ローカルパッケージの名前解決にも使える
おそらく go modulesを使えば、私のGoに対するイライラの少しが解決する

Goland設定

Lintなどを設定する

qiita.com

ここを見ればすぐに出来た。みなさんの知識ありがたい