gRPC(C++)をVisualStudioでビルド~HelloWorldまでやってみた

巷で流行りのgRPCに入門してみました。

C++C#で通信してみよー!ということで、まずは

hello_worldくらい適当に動かして・・・と思って実際やってみたんですが、

C++(VisualStudio)はhello_world動かすだけで大変だったのでメモ。

 

 

grpcとprotocのビルドまで

日本語では↓の記事が最高です。

gRPC 1.2.3をVisual Studio C++(2015)でビルドする - Qiita

更新日時も4/27と近いのでこれは勝ったなと思ったのですが、

これgRPCのビルド手順までなんですよねー。

hello_worldはvcprojも無いのでそこからも大変でした。orz

 

あと、↑の通りgRPC1.2.3でビルドすればよかったんですが、

gRPCのGitHubに行くと1.3.1まで出ていたので、

欲を出してそちらで通そうとしてしまったのも失敗しました。

1.3.xはgrpc.slnに含まれるaresとgrpc++_error_detailsあたりがエラーで通りません。

とりあえず通すだけなら2つのプロジェクトをアンロードすれば通ったので、

それでも良いかもしませんが、なんか気持ち悪いので自分は1.2.xでビルドしました。

(1.2.3と1.2.5で通ることを確認しました。win7,win10でそれぞれ確認)

 

でgRPCのビルドまでで上記記事と違う感じにした部分を上げていきます。

  1. gRPCリポジトリをZipでなくでGit Clone(Source Tree)で落とした
    →落とした後にタグからv1.2.xをチェックアウトします
     こちらだとProtoも一緒に落ちてくるので色々面倒くさくなくて良いです
  2. gRPCのビルド時のエラーで追加をしなくても通った(1.2.5/win10のみ確認)
    →上記記事だとgpr_mpscq_initといくつかのものをdefに足したとありましたが、こちらでは足さなくてもhello_worldは動きました(もしかしたらもっと複雑なものを作るときには必要なのかも)

くらいでしょうか・・・。もちろん記事のまままでも通ると思います。

CMakeの使い方も図付きで載っていたのでとても助かりました。

 

 

hello_worldのビルド

で、ここからが本番。hello_worldを通します。

と、色々書きそうな雰囲気を醸し出してますが、こちらもGitHub

超スバラシイものが上がっていたのでペタリしておきます。

GitHub - jozefizso/grpc-windows: Build gRPC on Windows x64

設定がpropsファイルにまとめられているのがGoodですね。

 

上記リポジトリを落としてきて、

  1. lib_debug.props/lib_release.propsを編集
    →<AdditionalIncludeDirectories>の値を自分の環境のgrpcとprotoのヘッダファイルがある場所にそれぞれ変更
    →<AdditionalLibraryDirectories>の値を自分の環境のgrpcとprotoのlibファイルがある場所にそれぞれ変更
  2. test_protoc.batを編集
    →protoc_pathを自分の環境のprotoc.exeがある場所に変更
    →grpc_protoc_plugins_path自分の環境のgrpc_cpp_plugin.exeがある場所に変更
  3. 編集したtest_protoc.batを実行
    →srcフォルダに以下4つのファイルができていればOK
    helloworld.grpc.pb.cc
    helloworld.grpc.pb.h
    helloworld.pb.cc
    helloworld.pb.h
  4. ビルド→実行

上記手順を行えば実行できました。素晴らしい...。

デフォルトx64しかない気がした(?)ので、

x86ビルドしたい場合はソリューション構成から追加してください。

 

上記プロジェクトでなくて自分でプロジェクト作成からやる場合、

結局はpropsの設定とopensslとzlibを落とせばOKです。

  1. プロジェクト作成してソースやらを入れる
  2. nugetでopensslとzlibをインストー
    grpc.dependencies.opensslで検索すると出てくるので、
     grpc.dependencies.openssl.1.0.204.1をインストールします
     そうすると他の3つも同時に落ちてきます。
    →もしくはgrpcビルド時にnugetで落としたものにパスを通しても良いと思います
  3. プリプロセッサ定義に_WIN32_WINNT=0x0600;を追加
    →これがないとビルドエラーになります。0x600は本当は環境に合わせて変更したほうが良いかも?

    WINVER, _WIN32_WINNT の設定値 - Qiita

  4. includeパス設定
    →grpcとprotoのヘッダーへのパスを追加します
  5. linkパス設定
    →grpcとprotoのライブラリへのパスを追加します
  6. 追加のライブラリ設定
    →以下を追加します
    ・libprotobufd.lib(releaseではdがないので注意)
    ・grpc++.lib
    ・grpc.lib
    ・gpr.lib
    ・ws2_32.lib
  7. ランタイムライブラリ設定変更
    →MTに変更(デバッグはMTd)
  8. protoのコンバート
    https://github.com/grpc/grpc/tree/master/examples/cpp/helloworld
     ↑にコマンド載ってるのでそれでコンバートしてください
     上記に書いてある通り4ファイルできればOKです
  9. ビルド→実行

 

 

とりあえずここまでで自分の環境だと実行できました。

 

C++サーバーとC#クライアントで通信

上記まででC++のserverをビルドできていれば、それを立ち上げた状態で

C#のhello_worldのクライアントを動かすと「Greeting:Hello you」と出てきて成功します。

 

C#のクライアントのビルドですが・・・はい。

exampleのGreeter.slnを開いて実行するだけで動きます。

C++での苦労はなんだったのか・・・と思うくらい簡単に動きます。

 

おわりに

hello_worldがとりあえず動いたので、これからはその他のサンプルを見たり、

適当なアプリを作っていけるかと思います。

次はきっとProtoの文法を覚えていくのが良いんでしょうか・・・?

色々作りながら覚えていかないとなーという感じです。

ReactivePropertyのPropertyChanged発生タイミングについて

初めてReactivePropertyを使っていて盛大にハマったのでメモ。

(.Net4.0なのでReactivePropertyは2.9を使用しています)

 

ReactivePropertyのOnNext実行と、PropertyChangedイベント発生タイミングって必ずしも同じではないんですね。

 

WPFでLivetを使ってVliewModelクラス内で以下のようなコードを書いた。

(IDisposable関連の処理は省いてるのと、外部からInitParam???とか色々あると思いますがテストなので)

public class HogeViewModel : Livet.ViewModel {
    public ReactiveProperty<Int32> Num;

public HogeViewModel(){ Num = new ReactiveProperty<Int32>(0); } public void InitParam(Int32 num){ //前に登録したイベントの解除とか Num.Value = num; // Value代入後にPropertyChangedイベントリスナ登録(CompositeDisposableに入れてね) new Livet.EventListeners.PropertyChangedEventListener(Num, (s, e) => RaisePropertyChanged(e.PropertyName)); } }

 

Num.Valueの代入後にEventListenerを登録しているので、

イベントは飛んでこないと思ったのですが、なぜか飛んでくる・・・。

しかもよくわからないタイミングで・・・。

System.Reactive.Core.dll!System.Reactive.Concurrency.Scheduler.Invoke(System.Reactive.Concurrency.IScheduler scheduler, System.Action action) 不明

( ^ω^)・・・???

 

色々試したところ以下で解決。

  1. PropertyChangedイベントを捕まえるのではなくReactivePropertyのSubscribe(OnNext)で処理する
  2. ReactivePropertyのSchedulerをImmidiateSchedulerかCurrentThredSchedulerを設定する(本当にこれで良いのか怪しい)

1.はあたりまえですね。というかReactivePropertyを使いながらなぜEventListenerを使っているのか・・・。

(あーその時はSubscribeだとPropertyNameが解らないじゃん!とか思ったからかもです。後で考えたら別に解らなくてもそこまで困らないですね。)

多分これが正解なはず。

 

2.ですが、ReactivePropertyのSetValueの処理は以下のようになっています。

this.Source.OnNext(value);
this.RaiseEventScheduler.Schedule(() => this.PropertyChanged?.Invoke(this, SingletonPropertyChangedEventArgs.Value));

 

OnNextはSetValueが呼び出された時に即時呼び出されますが、

PropertyChangedはRaiseEventSchedulerなるものに投げ込まれています。

これがデフォルトではImmidiateではないっぽいです。

作者の方々の説明ページもあったのですが、Rxに詳しくないのでいまいちピンときてません。

かずきさんにコメントいただきまして、UIスレッド外からのPropertyChangedイベント実行でエラーになるプラットフォームがあるためのこと。

ReactivePropertyで自動でUIスレッドにイベント発行を変えるのを抑止する - かずきのBlog@hatena

Reactive Extensions再入門 その45「Scheduler」 - かずきのBlog@hatena

Schedulerに関しては以下のページを参考に。

Rx入門 (15) - スケジューラの利用 - xin9le.net

Reactive Extensions再入門 その45「Scheduler」 - かずきのBlog@hatena

 

とりあえずReactiveProperty生成時に下記のようにしてやればこちらの望んでいた処理になりました。(元に戻す必要あるのかしら?)

var scheduler = Reactive.Bindings.ReactivePropertyScheduler.Default;
Reactive.Bindings.ReactivePropertyScheduler.SetDefault(System.Reactive.Concurrency.Scheduler.Immediate);
Num = new ReactiveProperty<Int32>(0);
Reactive.Bindings.ReactivePropertyScheduler.SetDefault(scheduler);

もしくは

Num = new ReactiveProperty<Int32>(0, System.Reactive.Concurrency.Scheduler.Immediate);  

 

ImmidiateはCurrentThreadに変えても挙動は同じでした。

ただ、アプリの初期化でSetDefaultすればよいとあったので、MainWindowのコンストラクタでSetDefaultしたのですが挙動がなぜか変わらず・・・。ReactivePropertyの生成時に設定するようにしたらうまくいきました。

アプリの初期化なのでApp.xaml.csのOnStartupで処理するのが良いようです。

(そういえばWPFで初期化処理書いたことなかった・・・)

 

とりあえずSubscribeで処理するのが良いとして、デフォルトでImmidiateやCurrentThreadでないのってなぜなんでしょうね。

OnNext実行とPropertyChanged発生が同一タイミングでないことのメリットってあるのだろうか・・・。

うーん。難しい。

 

VS2017RCのC#7で遊んでみた(ついでにValueTupleでハマったのでメモ)

VisualStudio2017RCが出たのでC#7でちょっと遊んでみました。

Visual Studio 2017 リリース ノート

 

ch9の↓の動画でも今回のRCで何の機能が入っているか解りますね。

Connect(); // 2016

 

そのほかC#7の新機能については安定のufcppさんのページで。

C# 7 の新機能 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C

 

値タプル(ValueTuple)使うときにハマった

で、早速値タプル使おうと思ったんですが、なぜかビルドエラーになる。

__DEMO__とかもう要らないよな~とか思いつつ足してみるもエラー。

なんでだ~と思っていたら、System.ValueTupleの参照が必要なんですね。

知らなかった・・・。ビルドエラーちゃんと読めば書いてあったんですが、

その時は使えないのかと思ってしまいました。

タプル構文 - 多値戻り値のサポート - xin9le.net

こちらにも書いてありますね・・・。

  • ローカル関数
  • outのインライン宣言
  • 型switch

などは普通に使えてこちらもとても便利便利。

インテリセンスで変数、関数などでフィルターがかけられるようになったのも

地味に便利です。

VS2017RC素晴らしいです。

 

C#ではないですが、C++ではビルド高速化、リンク時間30%削減と

こちらは仕事で試してみたい案件。

Visual Studio “15” で C++ ソリューションの読み込みとビルドを高速化 – Visual Studio 日本チーム Blog

 

T4メモ

T4テンプレートについて軽く調査したのでメモ

Visual Studio搭載のT4テンプレートエンジンの3通りの活用方法 - seraphyの日記

Visual Studio × T4 × 属性で Entity コード大量生成 - Qiita

基本は上つ2つのページに全て書いてある。

あとメタプログラミング.NET本。

(ランタイムテンプレートのサンプルがほぼないのが残念)

 

テンプレートの種類

テンプレートの種類は2種類

  1. テキストテンプレート(設計時テキストテンプレート)
  2. ランタイムテンプレート(実行時テンプレート)

テキストテンプレート(設計時テキストテンプレート)

ソリューションのビルドプロセスの過程でコンパイル、変換される。

.cs以外にも.txtや.cpp等々を生成可能。

コマンドやデータの追加に不随する処理の追加等を自動生成することに使用できそう。

後は、C#で書いたModel(データ)の定義からC++等の別の言語の定義や処理の自動生成とか。

ランタイムテンプレート(実行時テンプレート)

実行時にテンプレートに記述されている変数等を評価して文字列(String)を生成する。

データのフォーマット変換とかに使える感じかなと。

データの変換部分をテンプレート化しておいて、任意のデータ読み込んで、

テンプレートで自動生成されたpartialクラスに読み込んだデータ突っ込んで、

TransformTextするといったような感じでしょうか。

あとはRazorでやっているようなこととかをこっちに置き換えると、

間違った記述をしたときにコンパイルエラーにしてくれるので便利。

その他

csproj.user を作るための T4 テンプレート | kazuk は null に触れてしまった

こちらにもおもしろいT4の書き方が載っていたのでめも

よく訓練されたT4使いは 「何を元に作るか」 「何を作るか」 だけを考える。

<# Generate( (何を元に作るか), (o)=>{#> 何を作るか <#} ); #> と書く。

 

メタプログラミング.NET

メタプログラミング.NET