これは何?
go を書いていて、初めて decltype ほしいよと真剣に思ったので、その記録。
decltype ってのは、C++ にある言語要素で、式からその式の型を得る機能。
using usec_t = decltype (timeval{}.tv_usec);
のようにすると、usec_t
を 構造体 timeval
のメンバ tv_usec
の型にすることができる。便利。
そして。
にも参加しています。
ほしいよと思った経緯。
unix.Timeval
を使いたい
go で unix.Timeval
を使おうと思った。
まあ使おうと思った時点で負けという気もちょっとするけど、まあ使おうと思った。
float64
の値をゴニョゴニョして作った値で unix.Timeval
を初期化したい。
unix.Timeval
は、 Sec
と Usec
というメンバを持っていてこれに値を入れたい。
ターゲット
しかしターゲット環境は
- linux 386
- linux amd64
- darwin amd64
の三種(だということにする)。
それぞれの環境での Sec
Usec
の型は下表のとおり。
型 \ 環境 | linux 386 | linux amd64 | darwin amd64 |
---|---|---|---|
Sec |
int32 |
int64 |
int64 |
Usec |
int32 |
int64 |
int32 |
C/C++ と違って型が違う変数への代入は全面禁止。もとの値の interval
は float64
書きたかったコード
type SecType = なんか
type UsecType = なんか
t := unix.Timeval{
Sec: SecType(math.Floor(interval)),
Usec: UsecType((interval - math.Floor(interval)) * 1e6),
}
こうしたい。
こうしたいんだが、「なんか」の場所に typeOf(unix.Timeval{}.Sec)
などと書くことはできない。
実際にできること
仕方ないので
-
unsafe.Sizeof
などを駆使してなんとかする - 条件コンパイルで
UsecType
とUsecType
を適宜定義する - go generate でなんとかできるのかも。よく知らない。
の三択から選ぶことになるんだと思う。他にもあるかな。
私は条件コンパイルを選んだ。
こういうことに頭を使わせないでほしいと思う。
go1.18 以降なら
今なら、 Generics が使えるので
type SecConstraint interface {
int32 | int64
}
type UsecConstraint interface {
int32 | int64
}
func hoge[S SecConstraint, U UsecConstraint](s *S, u *U, interval float64) {
*s = S(math.Floor(interval))
*u = U((interval - math.Floor(interval)) * 1e6)
}
としておけば
t := unix.Timeval{}
hoge(&t.Sec, &t.Usec, interval)
と書ける。
しかしトリッキーな感じは否めない。
やっぱり前述の「書きたかったコード」のようにしたいよなぁと思う。