LoginSignup
12
15

More than 3 years have passed since last update.

Go で各 OS 向けライブラリをビルドして使う

Last updated at Posted at 2017-02-24

モチベーション

STORM.001.jpeg

メリット

  • 一回書けば Win/Mac 両方で動作する(Write once, run anywhere)
    • Java と違い各プラットフォームネイティブのバイナリが出力される
  • ネイティブのバイナリが出力されるので Java より高速
  • 実装が統一される

デメリット

主として Windows 側に集中しています。

  • Windows でのビルド方法がやや特殊
    • ひと手間(非公式の手段)かける必要性があり若干の懸念あり
    • これは、Go が Windows 向けの共有ライブラリを生成できない(buildmode=c-shared を非サポート)のと、C# が Win32 形式での静的ライブラリを利用できないためです。
  • Windows(と言うか C# というか CLR)では C# で書くよりパフォーマンスの懸念あり
    • アンマネージド DLL の呼出しなのでマーシャリングのコストが余計にかかるのではとの予想です。
    • マルチプラットフォーム対応でなく速度重視なら、おとなしく C#(というか CLR で動作する言語)で書いた方が高速だと思います。

Windows

呼び出し側は C# をターゲットにしています。

Go ライブラリのコード

rest.go
package main

import (
    "C"
    "fmt"

    "gopkg.in/resty.v0"
)

//export GetUsers
func GetUsers(p *C.char) *C.char {
    fmt.Println(C.GoString(p))
    resp, err := resty.R().
        SetHeader("Accept", "application/json").
        Get("https://jsonplaceholder.typicode.com/users")
    if err != nil {
        return C.CString("")
    }

    return C.CString(resp.String())
}

func init() {}
func main() {}

適当なウェブサービスを呼び出して、その結果を JSON で受け取り文字列として返却する関数です。

  • 関数のコメント重要
    • ここでエクスポートを宣言している(cgo)
  • C 言語を意識する
    • データ型の変換に注意

Go ライブラリのビルド

この手順は公式の方法です。

go build -buildmode=c-archive rest.go

DLL のビルド

この手順は非公式な方法です。
以下のツールをインストールしてください。

  • TDM-GCC
    • 環境変数 PATHbin/ のパスを通してください。
    • 一時的に設定する場合は set PATH=%PATH%;C:\TDM-GCC-64\bin です。
  • Visual Studio Community
    • インストール後に C++ のプロジェクトを作成して関連ツールをインストールしてください。

まずモジュール定義 (.def) ファイルを作成します。

rest.def
LIBRARY   rest
EXPORTS
   GetUsers

関数は EXPORTS 以下に追加していきます。

DLL をビルドします。

gcc -m64 -shared -o rest.dll rest.def rest.a -Wl,--allow-multiple-definition -static -lstdc++ -lwinmm -lntdll -lWs2_32

Golang で Windows の DLL を作る方法

C# から DLL を呼び出す

Visual Studio での注意

  • IDE の参照メニューからは DLL への参照を追加できないので(アンマネージドだから?)、実行ファイル(.exe)があるフォルダ(Debug)にビルドした DLL をコピーします(実行パスにある DLL はデフォルトで読み込まれる動作を利用)。
  • プラットフォーム構成の種類が Any CPU だと実行時例外が発生するので、 x64 に変更します(DLL が x64 向けにビルドされているので)。
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SampleGoDLL
{
    static class Program
    {
        [DllImport("rest.dll")]
        public static extern void PrintHello(string pStr);
        [DllImport("rest.dll")]
        public static extern IntPtr GetUsers(string pStr);

        /// <summary>
        /// アプリケーションのメイン エントリ ポイントです。
        /// </summary>
        [STAThread]
        static void Main()
        {
            IntPtr pRet = GetUsers("Call from C#");
            String json = Marshal.PtrToStringAnsi(pRet);
            Console.Write(json);
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

アプリケーションの起動時に実行されるようにしました。
関数の外部参照宣言(extern)毎に DLLImport 属性が必要です。
C# に関する詳細はインターネット上の情報を参照してください。

その他

DLL の検証

C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\x86_amd64 を環境変数 PATH に追加しておくと便利です。

  • DLL で宣言されている関数の確認する
    • dumpbin.exe /exports rest.dll
  • DLL のターゲットアーキテクチャを確認する
    • dumpbin.exe /headers rest.dll | findstr machine

macOS

呼び出し側は Objective-C をターゲットにしています。
Go ライブラリのコードおよび Go ライブラリのビルドは Windows と共通です。
macOS の場合は共有ライブラリの出力も可能ですが、Windows に合わせて静的ライブラリを出力します。

Objective-C から静的ライブラリを呼び出す

Xcode のプロジェクト設定

Xcode の詳しい使い方はインターネット上の情報を参照してください。

  • 静的ライブラリ(rest.a)を追加します。
  • ヘッダーファイル(rest.h)を追加します。
ViewController.m
#import "ViewController.h"
#import "rest.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Do any additional setup after loading the view.
    char *pStr = GetUsers("Call from Objective-C");
    NSLog(@"%s", pStr);
}
@end

ビューのロード時に実行されるようにしました。
ポイントはヘッダーファイルのインポートです。

最後に

Go は高速性とか並列性とかが注目されがちですが、こういう便利な機能ももっと注目されて良いと思います。

12
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
15