;

[FlatBuffers] 플랫버퍼란 무엇인가? 본문

C, C++, C#

[FlatBuffers] 플랫버퍼란 무엇인가?

WindowsHyun 2018. 9. 9. 01:59
반응형

 FlatBuffers


FlatBuffers 는 C, C++, C#, GO, Java, JavaScript, Lobster, Lua, TypeScript, PHP, Python, Rust를 위한 

크로스 플랫폼 직렬화 라이브러리 입니다.


FlatBuffers 플랫버퍼를 사용하는 이유?

    • 패킹/언 패킹 없이 직렬화 된 데이터에 엑세스
    • 메모리 효율성 및 속도 증가
    • 생성된 코드가 작고 단일 Header 파일로 쉽게 통합을 할 수 있습니다.
    • 모든 언어에서 사용하기 편리하게 제공 합니다.
    • 크로스 플랫폼, 종속성 없이 사용이 가능합니다.

FlatBuffers 사용 방법 :


  1. FlatBuffer Schema 작성

- Schema를 작성 하게 되면 자신이 원하는 플랫폼으로 라이브러리가 생성 됩니다.

namespace MyGame.Sample;

enum Color:byte { Red = 0, Green, Blue = 2 }

union Equipment { Weapon } // Optionally add more tables.

struct Vec3 {
  x:float;
  y:float;
  z:float;
}

table Monster {
  pos:Vec3;
  mana:short = 150;
  hp:short = 100;
  name:string;
  friendly:bool = false (deprecated);
  inventory:[ubyte];
  color:Color = Blue;
  weapons:[Weapon];
  equipped:Equipment;
}

table Weapon {
  name:string;
  damage:short;
}

root_type Monster;

위 내용은 FlatBuffers의 Schema 예제 중 하나인 monster.fbs 파일의 코드 입니다.

코드에서 보시는 바와 같이 직관적이게 보입니다.


간단히 설명을 하면 Monster라는 Table에 "위치, 마나, 체력, 이름, 친구, 인벤토리, 색상, 무기, 장비" 등이 들어 가 있습니다.

위 와 같이 테이블을 작성하고, 생성을 하시면 각 플랫폼에 맞게 라이브러리가 생성이 됩니다.


// automatically generated by the FlatBuffers compiler, do not modify


#ifndef FLATBUFFERS_GENERATED_FLATBUFFERS_MYGAME_SAMPLE_H_
#define FLATBUFFERS_GENERATED_FLATBUFFERS_MYGAME_SAMPLE_H_

#include "flatbuffers/flatbuffers.h"

namespace MyGame {
namespace Sample {

struct Vec3;

struct Monster;

struct Weapon;

enum Color {
  Color_Red = 0,
  Color_Green = 1,
  Color_Blue = 2,
  Color_MIN = Color_Red,
  Color_MAX = Color_Blue
};

inline Color (&EnumValuesColor())[3] {
  static Color values[] = {
    Color_Red,
    Color_Green,
    Color_Blue
  };
  return values;
}

inline const char **EnumNamesColor() {
  static const char *names[] = {
    "Red",
    "Green",
    "Blue",
    nullptr
  };
  return names;
}

inline const char *EnumNameColor(Color e) {
  const size_t index = static_cast(e);
  return EnumNamesColor()[index];
}

enum Equipment {
  Equipment_NONE = 0,
  Equipment_Weapon = 1,
  Equipment_MIN = Equipment_NONE,
  Equipment_MAX = Equipment_Weapon
};

template<> inline const Weapon *Monster::equipped_as() const {
  return equipped_as_Weapon();
}

inline flatbuffers::Offset CreateMonster(
    flatbuffers::FlatBufferBuilder &_fbb,
    const Vec3 *pos = 0,
    int16_t mana = 150,
    int16_t hp = 100,
    flatbuffers::Offset name = 0,
    flatbuffers::Offset> inventory = 0,
    Color color = Color_Blue,
    flatbuffers::Offset>> weapons = 0,
    Equipment equipped_type = Equipment_NONE,
    flatbuffers::Offset equipped = 0) {
  MonsterBuilder builder_(_fbb);
  builder_.add_equipped(equipped);
  builder_.add_weapons(weapons);
  builder_.add_inventory(inventory);
  builder_.add_name(name);
  builder_.add_pos(pos);
  builder_.add_hp(hp);
  builder_.add_mana(mana);
  builder_.add_equipped_type(equipped_type);
  builder_.add_color(color);
  return builder_.Finish();
}

inline flatbuffers::Offset CreateMonsterDirect(
    flatbuffers::FlatBufferBuilder &_fbb,
    const Vec3 *pos = 0,
    int16_t mana = 150,
    int16_t hp = 100,
    const char *name = nullptr,
    const std::vector *inventory = nullptr,
    Color color = Color_Blue,
    const std::vector> *weapons = nullptr,
    Equipment equipped_type = Equipment_NONE,
    flatbuffers::Offset equipped = 0) {
  return MyGame::Sample::CreateMonster(
      _fbb,
      pos,
      mana,
      hp,
      name ? _fbb.CreateString(name) : 0,
      inventory ? _fbb.CreateVector(*inventory) : 0,
      color,
      weapons ? _fbb.CreateVector>(*weapons) : 0,
      equipped_type,
      equipped);
}

inline const MyGame::Sample::Monster *GetMonster(const void *buf) {
  return flatbuffers::GetRoot(buf);
}

inline bool VerifyMonsterBuffer(
    flatbuffers::Verifier &verifier) {
  return verifier.VerifyBuffer(nullptr);
}

inline void FinishMonsterBuffer(
    flatbuffers::FlatBufferBuilder &fbb,
    flatbuffers::Offset root) {
  fbb.Finish(root);
}

}  // namespace Sample
}  // namespace MyGame

#endif  // FLATBUFFERS_GENERATED_FLATBUFFERS_MYGAME_SAMPLE_H_

위 예제는 monster.fbs Schema를 C++로 컴파일 하여 나온 Header 파일 중 일부를 지운 내용 입니다.


Schema에서 사용 할 수 있는 문법은 아래와 같이 제공 됩니다.

schema = include* ( namespace_decl | type_decl | enum_decl | root_decl | file_extension_decl | file_identifier_decl | attribute_decl | rpc_decl | object )*

include = include string_constant ;

namespace_decl = namespace ident ( . ident )* ;

attribute_decl = attribute ident | "ident" ;

type_decl = ( table | struct ) ident metadata { field_decl+ }

enum_decl = ( enum ident [ : type ] | union ident ) metadata { commasep( enumval_decl ) }

root_decl = root_type ident ;

field_decl = ident : type [ = scalar ] metadata ;

rpc_decl = rpc_service ident { rpc_method+ }

rpc_method = ident ( ident ) : ident metadata ;

type = bool | byte | ubyte | short | ushort | int | uint | float | long | ulong | double | int8 | uint8 | int16 | uint16 | int32 | uint32| int64 | uint64 | float32 | float64 | string | [ type ] | ident

enumval_decl = ident [ = integer_constant ]

metadata = [ ( commasep( ident [ : single_value ] ) ) ]

scalar = integer_constant | float_constant

object = { commasep( ident : value ) }

single_value = scalar | string_constant

value = single_value | object | [ commasep( value ) ]

commasep(x) = [ x ( , x )* ]

file_extension_decl = file_extension string_constant ;

file_identifier_decl = file_identifier string_constant ;

integer_constant = -?[0-9]+ | true | false

float_constant = -?[0-9]+.[0-9]+((e|E)(+|-)?[0-9]+)?

string_constant = \".*?\\"

ident = [a-zA-Z_][a-zA-Z0-9_]*

출처 : FlatBuffers Grammar of the schema language


  2. Schema 컴파일 방법


Schema를 자신이 원하는 플랫폼에 컴파일 방법은 우선 Release된 

FlatBuffers를 다운로드 부 터 시작 됩니다.

다운로드 주소 : Github FlatBuffers Releases


↑ 다운로드 된 파일을 보시면 flatc 라 는 파일이 있습니다.


↑ 해당 파일을 풀어 놓으시고, 해당 위치에 작성된 Schema인 *.fbs를 같이 놓으시면 됩니다.


↑ 그 뒤 Shift+오른쪽 마우스를 클릭하여 "여기서 명령창 열기"를 클릭하시면 됩니다.



flatc [ GENERATOR OPTIONS ] [ -o PATH ] [ -I PATH ] [ -S ] FILES...

      [ -- FILES...]

↑ Schema를 원하시는 언어로 컴파일 하시려면 컴파일 방법을 알아야 합니다.


GENERATOR OPTIONS : 원하는 언어

--cpp, -c : Generate a C++ header for all definitions in this file (as filename_generated.h).
--java, -j : Generate Java code.
--csharp, -n : Generate C# code.
--go, -g : Generate Go code.
--python, -p: Generate Python code.
--js, -s: Generate JavaScript code.
--ts: Generate TypeScript code.
--php: Generate PHP code.
--grpc: Generate RPC stub code for GRPC.
--dart: Generate Dart code.
--lua: Generate Lua code.
--lobster: Generate Lobster code.
--rust, -r : Generate Rust code.

-o PATH : 저장될 위치

-I PATH : Schema *.fbs가 있는 위치


ex) Schema를 통하여 C++로 컴파일을 한다.

- flatc --cpp -o ./cpp/ ./monster.fbs


ex) Schema를 통하여 C#으로 컴파일을 한다.

- flatc --csharp -o ./csharp/ ./monster.fbs


↑ 입력을 하신 후 Enter 를 칩니다.


↑ cpp 폴더 안에 monster_generated.h 파일이 생성이 됩니다.



매번 코드를 입력하기 위하여 사이트를 들어오셔서 복사를 하시는 분들을 위하여

번호 하나만 치시면 자동으로 컴파일 되는 프로그램을 직접 만들었습니다.


↑ input_fbs에 "FlatBuffers.fbs"로 파일을 저장하신 후 "MakeFlatBuffers.bat" 파일을 실행 시키셔서 

자신이 원하는 언어 번호를 치시면 "output_file"에 원하시는 언어로 자동 컴파일 됩니다.


다운로드 : FlatBuffers_MakeFile.zip





  3. 컴파일 된 Header를 적용 시켜 보자.

- C++ 예제를 설명해 드리겠습니다.


↑ 컴파일 된 Header 파일을 자신이 만든 프로젝트에 붙어넣기 합니다.


↑ Header 파일을 불러오셔도 빨간 줄 이 나오면서 빌드를 할 수가 없습니다.


이유는 FlatBuffers를 사용하기 위한 사용자 Schema를 컴파일한 Header는 준비 되었지만 

FlatBuffers에 필요한 Header 파일이 들어가 있지 않기 때문 입니다.


↑ Source Code를 받으신 후


↑ 압축을 푸신 후 해당 폴더의 "include" 폴더에 들어가셔서 "flatbuffers"를 header파일을 복사 해놓은 위치에 같이 넣어 주세요.


↑ 넣으신 후 빌드를 하시면 정상적으로 빌드가 되실 겁니다.


#include 
#include "FlatBuffers_generated.h"

using namespace MyGame::Sample;

int main() {
	//---------------------------------------------------------------------------------------------------------------
	// FlatBuffers 데이터 만들기.

	// FlatBuffers 빌드를 선언 해줍니다.
	flatbuffers::FlatBufferBuilder builder;

	// 몬스터에게 검, 도끼 에 대한 무기와 데미지를 넣어 줍니다.
	auto weapon_one_name = builder.CreateString("Sword");
	short weapon_one_damage = 3;

	auto weapon_two_name = builder.CreateString("Axe");
	short weapon_two_damage = 5;

	// `CreateWeapon` 명령어를 사용하여 모든 필드가 설정된 무기를 만듭니다.
	auto sword = CreateWeapon(builder, weapon_one_name, weapon_one_damage);
	auto axe = CreateWeapon(builder, weapon_two_name, weapon_two_damage);

	// `std::vector`에서 FlatBuffer의 `vector`를 만듭니다.
	std::vector> weapons_vector;
	weapons_vector.push_back(sword);
	weapons_vector.push_back(axe);
	auto weapons = builder.CreateVector(weapons_vector);

	// 몬스터에 필요한 좌표, 이름, 인벤토리 등을 만들어 줍니다.
	auto position = Vec3(1.0f, 2.0f, 3.0f);

	auto name = builder.CreateString("MyMonster");

	unsigned char inv_data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	auto inventory = builder.CreateVector(inv_data, 10);

	// 모든 데이터가 설정된 후 몬스터 생성을합니다.
	auto orc = CreateMonster(builder, &position, 150, 80, name, inventory,
		Color_Red, weapons, Equipment_Weapon, axe.Union());

	builder.Finish(orc);  // 해당 데이터를 빌드 합니다.
	//---------------------------------------------------------------------------------------------------------------
	// FlatBuffers 데이터 받기
	
	// monster라는 변수에 빌드된 데이터를 받아서 불러옵니다.
	auto monster = GetMonster(builder.GetBufferPointer());

	std::cout << "몬스터 체력 : " << monster->hp() << std::endl;
	std::cout << "몬스터 마나 : " << monster->mana() << std::endl;
	std::cout << "몬스터 이름 : " << monster->name()->str() << std::endl;
	std::cout << "몬스터 위치 : " << monster->pos()->x() << ", " << monster->pos()->y() << ", " << monster->pos()->z() << std::endl;
}

↑ "monster.fbs" Schema를 C++로 컴파일 하여 Monster 테이블에 값을 넣고 불러오는 예제 코드입니다.


↑ 예제 코드를 실행하면 위와 같이 값을 출력해 줍니다.


예제 코드 다운로드 (Visual Studio 2017 컴파일 ) : FlatBuffersTest.zip




반응형
Comments