LoginSignup
10
9

More than 5 years have passed since last update.

C++でデータを保存する(Protocol Buffers編)

Last updated at Posted at 2014-08-21

Protocol BuffersはGoogleが開発しているフリーのシリアライズライブラリ。
構造を定義したファイルをprotocでコンパイルしてコードを生成して利用する。

cocos2d-xで利用するにはiOS/Android用のバイナリを作成する必要がある。

URL

Protocol Buffersをマシン上で利用する

環境

  • Mac OS X 10.9.4

インストール

  • brewでinstall

    以下で完了。

    brew install protobuf
    
  • ソースからコンパイルしてinstall
    ここから最新のものをダウンロードして解凍。以下ではバージョン2.5.0を選択。

    curl https://protobuf.googlecode.com/files/protobuf-2.5.0.tar.gz |  tar xvz
    

    解凍したソースをmakeしてインストール

    cd protobuf-2.5.0
    ./configure
    make
    make install
    

データの読み書きをする

以下、チュートリアル通り。

以下の内容のaddressbook.protoというファイルを作成する。

addressbook.proto
package tutorial;

message Person {
  required string name = 1;
  required int32 id = 2;
  optional string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    required string number = 1;
    optional PhoneType type = 2 [default = HOME];
  }

  repeated PhoneNumber phone = 4;
}

message AddressBook {
  repeated Person person = 1;
}

コマンドでコンパイル。以下ではカレントにソースを出力。

protoc -I=. --cpp_out=. addressbook.proto

addressbook.pb.hとaddressbook.pb.ccが生成されるのでこれらを利用する。

コンパイルは以下のコマンドで実行。

clang++ -g -Wall -std=c++11 -lprotobuf main.cc addressbook.pb.cc

iOS/AndroidでProtocol Buffersを利用する

iOS/Androidで利用するには別途コンパイルが必要。

iOS向けにコンパイル

スクリプトが公開されているのでそれを利用する。

こちらの環境では上記のスクリプトが動作しないのでこれを利用。

注意
上記スクリプトでコンパイルすると名前空間がgoogleからgoogle_publicに変更される

git clone https://gist.github.com/7150245.git build-protobuf
cd build-protobuf
sh ./build-protobuf-2.5.0.sh

protobufディレクトリ以下にバイナリ等が配置される。ライブラリはuniversalになるのでターゲットはどれでも利用できる。

protobuf/
├── bin
│   └── protoc
├── include(省略)
└── lib
    ├── libprotobuf-lite.a
    ├── libprotobuf-lite.la
    ├── libprotobuf.a
    ├── libprotobuf.la
    ├── libprotoc.a
    ├── libprotoc.la
    └── pkgconfig
        ├── protobuf-lite.pc
        └── protobuf.pc

Android向けにコンパイル

ほぼ、こちらの通り。

注意
前のiOSスクリプトを利用してコンパイルしていると名前空間が変更されるのでcocos2d-xでも利用するには/tmp/protobuf-*内のソースを利用する必要がある(undefinedエラーでコケる)

以下要点。

  • 解凍したディレクトリ名をjniに変更

    • undefinedエラーが出ないためには/tmp/protobuf-*内のソースを上書きコピーしておく
    cd jni
    cp -prf /tmp/protobuf-2.5.0/* .
    
  • 適当なディレクトリを作成し、そこにjniを移動

  • AndroidManifest.xml(適当なもの)を作成したディレクトリに配置

  • Android.mkをjniに配置

  • Application.mkをjniに配置

    • cocos2d-x v3.2だとc++_staticを利用しないとundefinedエラーが発生する
  • スクリプトを実行。${ANDROID_NDK_HOME}にはndkのパスを指定

  • ビルド

    ${ANDROID_NDK_HOME}/ndk-build
    

作成したバイナリはobj以下にできる。

obj/
└── local
    ├── armeabi
    │   └── libprotobuf.a
    ├── armeabi-v7a
    │   └── libprotobuf.a
    └── x86
        └── libprotobuf.a

Protocol Buffersをcocos2d-xで利用する

cocos2d/externalにライブラリを追加する

生成したファイルを以下のように配置する。

protobuf
├── include(省略)
└── prebuilt
    ├── android
    │   ├── Android.mk
    │   ├── armeabi
    │   │   └── libprotobuf.a
    │   ├── armeabi-v7a
    │   │   └── libprotobuf.a
    │   └── x86
    │       └── libprotobuf.a
    └── ios
        └── libprotobuf.a

Android.mkは以下の内容で作成した。

Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := protobuf_static
LOCAL_MODULE_FILENAME := protobuf
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libprotobuf.a
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../include
include $(PREBUILT_STATIC_LIBRARY)

プロジェクトにライブラリを追加する

cocosコマンドで新しいプロジェクトを作成しておく。

iOSに追加

  • XCodeで作成したプロジェクトを開き、protobufをcocos2d_libs.xcodeproj内のexternalにD&D
  • 「Add To Target」はチェックなしにして追加
  • 追加したprotobuf内のprebuilt/ios/libprotobuf.aファイルを選択して「Target Membership」のうちcocos2d Mac/cocos2dx iOSにチェックを入れる
  • コンパイルできることを確認する
    • エラーが出る場合はLibraryやHeader Search Pathsを確認する。

Androidに追加

  • cocos2d/cocos/Android.mkに以下を追加

    LOCAL_WHOLE_STATIC_LIBRARIES += protobuf_static
    $(call import-module,external/protobuf/prebuilt/android)
    
  • コンパイルできることを確認する

cocos2d-xからライブラリを呼び出す

  • protocで生成したファイルをClassesに追加
  • ここを参考にHelloWorld内の適当な所にコードを書く
HelloWorldScene.cpp
    // ファイル書き込み
    {
        tutorial::AddressBook address_book;
        for (auto i = 0; i < 5; ++i) {
            auto person = address_book.add_person();
            person->set_id(i);
            person->set_name(StringUtils::format("Name-%d", i));
            person->set_email(StringUtils::format("email-%d", i));
        }
        CCLOG("### %s", address_book.DebugString().c_str());

        auto filename = FileUtils::getInstance()->getWritablePath() + "addressbook.bin";
        std::fstream output(filename, std::ios::out | std::ios::trunc | std::ios::binary);
        if (!address_book.SerializeToOstream(&output)) {
            CCLOG("Failed to write address book.");
            return false;
        }
    }


    // ファイル読み込み
    {
        tutorial::AddressBook address_book;

        auto filename = FileUtils::getInstance()->getWritablePath() + "addressbook.bin";
        std::fstream input(filename, std::ios::in | std::ios::binary);
        if (!address_book.ParseFromIstream(&input)) {
            CCLOG("Failed to parse address book.");
            return false;
        }

        CCLOG("### %s", address_book.DebugString().c_str());

    }

    google_public::protobuf::ShutdownProtobufLibrary();
  • コンパイルして確認
10
9
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
10
9