Go
golang
プログラミング言語Go

プログラミング言語Goを読みながらメモ(第十三章)

プログラミング言語 Go を読みながらメモ。

第一章 : https://qiita.com/Nabetani/items/077c6b4d3d1ce0a2c3fd
第二章 : https://qiita.com/Nabetani/items/d053304698dfa3601116
第三章 : https://qiita.com/Nabetani/items/2fd9c372fcd8383955a5
第四章 : https://qiita.com/Nabetani/items/59bfd00dc3323883a07f
第五章 : https://qiita.com/Nabetani/items/4b785f1c9b0b26d48475
第六章 : https://qiita.com/Nabetani/items/1c100394a65af6506187
第七章 : https://qiita.com/Nabetani/items/6553ad253af77661e915
第八章 : https://qiita.com/Nabetani/items/3b2e3964159cc292fe00
第九章 : https://qiita.com/Nabetani/items/c94e51aa29926a56159e
第十章と第十一章は省略。
第十二章 : https://qiita.com/Nabetani/items/2d63279465e7ab8c20a2

で。

十三章は、楽しい unsafe。

ポインタを使ってプライベートメンバを書き換える

まずはこんなイタズラから。

go
package main

import (
    "fmt"
    "os"
    "reflect"
    "unsafe"
)

func main() {
    stdout := os.Stdout
    vs := reflect.ValueOf(stdout).Elem()
    file := vs.FieldByName("file")
    pfd := file.Elem().FieldByName("pfd")
    sysfd := pfd.FieldByName("Sysfd")
    unsafePtrToFD := unsafe.Pointer(sysfd.Addr().Pointer())
    intPtrToFD := (*int)(unsafePtrToFD)
    fmt.Fprintln(stdout, "stdout?") // 標準出力に出る
    *intPtrToFD = 2
    fmt.Fprintln(stdout, "stderr?") // 標準エラーに出る!
}

本当にこれで「stderr?」は標準エラーに出た。

unsafe.Pointer を使わないバージョンでは書き換え禁止だったけど、unsafe.Pointer を使ったら書き換えられた。素晴らしい。

とにかく、邪悪な用途に使えることはわかった。面白い。

cgo で、C のコードを呼ぶ

本よりも
http://otiai10.hatenablog.com/entry/2014/06/24/082448
が参考になった。

go
package main

/*
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char const * gcc_version(){
    static char r[] = "123.123.123";
    sprintf( r, "%d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ );
    return r;
}

char const * clang_version(){
    return __clang_version__;
}

int is_cpp(){
#if defined __cplusplus
    return 1;
#else
    return 0;
#endif
}

*/
import "C"
import "fmt"

func main() {
    gv := C.gcc_version()
    fmt.Printf("gcc_version : %v\n", C.GoString(gv))
    //=> gcc_version : 4.2.1
    cv := C.clang_version()
    fmt.Printf("clang versoin : %v\n", C.GoString(cv))
    //=> clang versoin : 9.1.0 (clang-902.0.39.1)
    fmt.Printf("is_cpp() == %v\n", C.is_cpp())
    //=> is_cpp() == 0
}

こんな感じ。

cgo で、C++ のコードを呼ぶ

C++ を go のソースに埋め込む方法はわからなかったので、別ファイルにした。

まずは C++

c++
// clang++ -c hoge.cpp -o hoge.o

extern "C"
int is_cpp(){
#if defined __cplusplus
  return 1;
#else
  return 0;
#endif
}

を、コメントのとおりにビルドしておいて、
go にリンクする。

go
package main

/*
#cgo CFLAGS:
#cgo LDFLAGS: hoge.o

int is_cpp();

*/
import "C"
import "fmt"

func main() {
    fmt.Printf("is_cpp() == %v\n", C.is_cpp())
}

と、ちゃんと C++ のコードが呼べる。

終りに

というわけで、読破した。
読破しただけなので、練習問題もやっていないし、まだ書けるという感じではない。
とはいえ、全体像はわかったような気になった。

本の評価はあんまり高くないんだけど、これより良い本があるような気もしないので仕方ないという感じ。

C++でいうところの「注解C++リファレンスマニュアル」、あるいは C でいうところの「S・P・ハービソン3世とG・L・スティール・ジュニアのCリファレンスマニュアル」のような本がほしい。
まあウェブを見ろということなんだと思う。