2021/05/04


Go ではポインタは扱えるけれど、ポインタ演算は扱えないというのが共通認識でした。もちろん unsafe.Pointeruintptr を使う事で、出来なくはなかったのですが簡単ではありませんでした。

package main

import (
    "unsafe"
)

type foo struct {
    k int64
    v int64
}

func main() {
    f := &foo{3,4}

    // unsafe.Pointer() で匿名ポインタにして
    // uintptr() で演算可能にして
    // +8 バイト(64bit)足して
    // unsafe.Pointer で匿名ポインタに戻して
    // そこにはフィールド v があるはずなので *int64 にキャストして
    // デリファレンスすれば出来上がり
    *(*int64)(unsafe.Pointer((uintptr(unsafe.Pointer(f))+8))) = 5 // グヒヒ

    println(f.v) // 5
}

Go 1.17 からは unsafe.Addunsafe.Slice が導入される事で、少しだけ簡単にポインタ演算そしてポインタからスライスを復元する事ができる様になります。(参照)

package main

import (
    "fmt"
    "unsafe"
)

func main() {
    b := []byte{12345}
    pb := &b[3]
    fmt.Println(unsafe.Slice(pb, 2)) // 4, 5

    // sizeof(byte) 分ポインタをずらす
    pb = (*byte)(unsafe.Add(unsafe.Pointer(pb), 1))
    fmt.Println(*pb) // 5

    v := []int{23456}
    pi := &v[3]
    // オフセット3からのスライスを得る
    fmt.Println(unsafe.Slice(pi, 2)) // 5, 6

    // sizeof(int) 分ポインタをずらす
    pi = (*int)(unsafe.Add(unsafe.Pointer(pi), 8))
    fmt.Println(*pi) // 6
}

もちろん不正なオフセットを指定して unsafe.Add を呼び出してデリファレンスしたり、不正なサイズを指定してスライスを復元したりすると panic する事になります。

Posted at by



2021/03/03


おなじみC/C++から使えるJSONライブラリを紹介するコーナー。まずは過去のまとめ。

これまで C++ から扱える JSON パーサを数多く紹介してきましたが、いずれもコメントを扱えるものはありませんでした。今回紹介する jsoncpp はコメントも扱えます。

GitHub - open-source-parsers/jsoncpp: A C++ library for interacting with JSON.

JsonCpp JSON is a lightweight data-interchange format. It can represent numbers, strings, ordered se...

https://github.com/open-source-parsers/jsoncpp

特徴は以下の通り。

  • コメントが扱える
  • Amalgamated code を生成できる
  • 古いコンパイラをサポートしている
  • Public Domain

触ってみた感じは json.hpp に近いです。

#include "json/json.h"
#include 

int
main(int argc, char* argv[]) {
  const std::string rawJson = R"(
  {
    "Age": 20,
    "Name": "mattn" // your name
  }
  )";
  Json::Value root;
  Json::CharReaderBuilder builder;
  builder["collectComments"] = true;
  JSONCPP_STRING errs;
  auto reader = builder.newCharReader();
  if (!reader->parse(rawJson.c_str(), rawJson.cend().base(), &root, &errs)) {
    std::cout << errs << std::endl;
    return EXIT_FAILURE;
  }
  std::cout << root["Age"].asInt64() << std::endl;
  std::cout << root["Name"].asString() << std::endl;
  std::cout << root["Name"].getComment(Json::CommentPlacement::commentAfterOnSameLine) << std::endl;
  return EXIT_SUCCESS;
}

collectComments を true にする事でパース時にコメントを収集してくれます。getComment に指定する commentBefore, commentAfterOnSameLine, commentAfter によりノードの前方、同一行または後方、後方、のいずれかのコメントを得られます。

jsoncpp にはこれだけでなく、とても便利な色々なオプションが用意されています。

collectComments: false or true

true でコメントを収集し、シリアライズ時にコメントを戻す

allowComments: false or true

コメントを許可

allowTrailingCommas: false or true

ケツカンマを許可

strictRoot: false or true

ルートノートに配列またはオブジェクトを必須とする

allowDroppedNullPlaceholders: false or true

"null" という文字列をシリアライズ時にドロップする。

allowNumericKeys: false or true

オブジェクトに数値のキーを許可

allowSingleQuotes: false or true

シングルクオートを許可

stackLimit: 数値

階層の限界を指定。オーバーすると例外が発生。

failIfExtra: false or true

余計な空白をエラーとして扱う。

rejectDupKeys: false or true

オブジェクトのキーに重複を許可。

allowSpecialFloats: false or true

NaN や inf の様な特殊な浮動小数点を許可。


これだけあれば多少ユルユル仕様な設定ファイル向けの JSON ファイルも読み込めると思います。

また jsoncpp は通常、ライブラリ形式(.a)が提供されますが、付属の amalgamate.py を使う事で、ヘッダファイル1つとソースファイル1つに纏められた形式にする事もできる為、以下の様に簡単にソースコードをビルドする事ができます。

$ ls -R
.:
json  jsoncpp.cpp  main.cpp

./json:
json.h  json-forwards.h

$ gcc -I. -o app main.cpp jsoncpp.cpp

jsoncpp は基本、パブリックドメインのライセンスで提供されています。しかし地域によってはパブリックドメインは実在しない扱いとなります。jsoncpp ではその様な地域の場合は MIT ライセンスのもと配布されるという事になっています。

ここまで融通の効く C++ 向け JSON パーサは僕が知る限り他には無いと思います。

江添亮のC++入門 (アスキードワンゴ) 江添亮のC++入門 (アスキードワンゴ)
江添 亮
ドワンゴ Kindle版 / ¥4,950 (2019年10月23日)
 
発送可能時間:

Posted at by



2020/10/30


Go 言語はシングルバイナリをウリにしたプログラミング言語です。バイナリファイルを1つポンと scp で転送すれば動くのでとても便利です。シングルバイナリとなると当然、画像や HTML といったアセットをバイナリに埋め込みたくなります。

Go 言語ではこれまで go-assetsgo-bindatastatik というツールを使う事でファイルのコンテンツをバイナリ化し、変数からアクセスする様にしてきました。

しかしそれらには色々な流儀や OS 間でのまばらな動作など、ユーザにとって納得のいかない物がありました。昨日、Go 言語ではオフィシャルとしてこのファイル埋め込みをサポートする様になりました。Go 1.16 から使える様になります。

cmd/go: add //go:embed support · golang/go@25d28ec · GitHub

+3 −3 src/cmd/go/internal/fsys/fsys.go +1 −1 src/cmd/go/internal/fsys/fsys_test.go +2 −0 src/cmd/go/...

https://github.com/golang/go/commit/25d28ec55aded46e0be9c2298f24287d296a9e47
package main

import (
    _ "embed"
    "net/http"

    "github.com/labstack/echo"
)

//go:embed static/logo.png
var contents []byte

func main() {
    e := echo.New()
    e.GET("/"func(c echo.Context) error {
        return c.Blob(http.StatusOK, "image/png", contents)
    })
    e.Logger.Fatal(e.Start(":8989"))
}

変数に //go:embed [ファイル名] というコメントを付ける事でそのコンテンツを変数に埋め込む事ができます。開発者がやるのは embed をブランクインポートする事と go build だけです。ファイルが読み込めなかったり、記法が不正な場合はコンパイルエラーとなります。

pattern static/logo1.png: no matching files found

またバイト列ではなく文字列でも使えます。

package main

import (
    _ "embed"
    "fmt"
)

//go:embed message.txt
var message string

func main() {
    fmt.Println(message)
}

これまで go generate を使ってバイナリファイルを変数に埋め込んでいたので、それから比べるとずいぶん便利になりました。そしてファイルだけでなく、ファイルシステムとして埋め込む事もできる様になりました。

package main

import (
    "embed"
    "io"
    "log"
    "os"
    "path"
)

//go:embed static
var local embed.FS

func main() {
    fis, err := local.ReadDir("static")
    if err != nil {
        log.Fatal(err)
    }
    for _, fi := range fis {
        in, err := local.Open(path.Join("static", fi.Name()))
        if err != nil {
            log.Fatal(err)
        }
        out, err := os.Create("embed-" + path.Base(fi.Name()))
        if err != nil {
            log.Fatal(err)
        }
        io.Copy(out, in)
        out.Close()
        in.Close()
        log.Println("exported""embed-"+path.Base(fi.Name()))
    }
}

embed.FS は実際にファイルが開ける Open メソッドをサポートしている為、 http.FileSystem が返す様なファイルシステムとは互換性がなく、以下の様にウェブサーバに使う事はできないですが、いずれサードパーティからラップするライブラリが登場すると思います。

package main

import (
    "embed"
    "net/http"

    "github.com/labstack/echo"
)

//go:embed static
var local embed.FS

func main() {
    e := echo.New()
    e.GET("/", echo.WrapHandler(http.FileServer(local)))
    e.Logger.Fatal(e.Start(":8989"))
}

追記 http.FS という関数が既に用意されていました。以下のリポジトリの example4 にサンプルを足しています。

Go 1.16 が待ち遠しいですね。

Go 1.16 が待ち遠しいですね。

※大事な事なので2回言いました。

これらのサンプルは以下のリポジトリに置いてあります。

GitHub - mattn/go-embed-example

We use optional third-party analytics cookies to understand how you use GitHub.com so we can build b...

https://github.com/mattn/go-embed-example
改訂2版 みんなのGo言語 改訂2版 みんなのGo言語
松木 雅幸, mattn, 藤原 俊一郎, 中島 大一, 上田 拓也, 牧 大輔, 鈴木 健太
技術評論社 Kindle版 / ¥2,350 (2019年08月01日)
 
発送可能時間:

Posted at by