;

[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를 작성 하게 되면 자신이 원하는 플랫폼으로 라이브러리가 생성 됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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에 "위치, 마나, 체력, 이름, 친구, 인벤토리, 색상, 무기, 장비" 등이 들어 가 있습니다.

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// 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<int><span style="font-family: "맑은 고딕", sans-serif;">(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</span><weapon><span style="font-family: "맑은 고딕", sans-serif;">() const {
  return equipped_as_Weapon();
}
 
inline flatbuffers::Offset</span><monster><span style="font-family: "맑은 고딕", sans-serif;"> CreateMonster(
    flatbuffers::FlatBufferBuilder &_fbb,
    const Vec3 *pos = 0,
    int16_t mana = 150,
    int16_t hp = 100,
    flatbuffers::Offset</span><flatbuffers::string><span style="font-family: "맑은 고딕", sans-serif;"> name = 0,
    flatbuffers::Offset</span><flatbuffers::vector<uint8_t><span style="font-family: "맑은 고딕", sans-serif;">> inventory = 0,
    Color color = Color_Blue,
    flatbuffers::Offset</span><flatbuffers::vector<flatbuffers::offset<weapon><span style="font-family: "맑은 고딕", sans-serif;">>> weapons = 0,
    Equipment equipped_type = Equipment_NONE,
    flatbuffers::Offset</span><void><span style="font-family: "맑은 고딕", sans-serif;"> 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</span><monster><span style="font-family: "맑은 고딕", sans-serif;"> CreateMonsterDirect(
    flatbuffers::FlatBufferBuilder &_fbb,
    const Vec3 *pos = 0,
    int16_t mana = 150,
    int16_t hp = 100,
    const char *name = nullptr,
    const std::vector</span><uint8_t><span style="font-family: "맑은 고딕", sans-serif;"> *inventory = nullptr,
    Color color = Color_Blue,
    const std::vector</span><flatbuffers::offset<weapon><span style="font-family: "맑은 고딕", sans-serif;">> *weapons = nullptr,
    Equipment equipped_type = Equipment_NONE,
    flatbuffers::Offset</span><void><span style="font-family: "맑은 고딕", sans-serif;"> equipped = 0) {
  return MyGame::Sample::CreateMonster(
      _fbb,
      pos,
      mana,
      hp,
      name ? _fbb.CreateString(name) : 0,
      inventory ? _fbb.CreateVector</span><uint8_t><span style="font-family: "맑은 고딕", sans-serif;">(*inventory) : 0,
      color,
      weapons ? _fbb.CreateVector</span><flatbuffers::offset<weapon><span style="font-family: "맑은 고딕", sans-serif;">>(*weapons) : 0,
      equipped_type,
      equipped);
}
 
inline const MyGame::Sample::Monster *GetMonster(const void *buf) {
  return flatbuffers::GetRoot</span><mygame::sample::monster><span style="font-family: "맑은 고딕", sans-serif;">(buf);
}
 
inline bool VerifyMonsterBuffer(
    flatbuffers::Verifier &verifier) {
  return verifier.VerifyBuffer</span><mygame::sample::monster><span style="font-family: "맑은 고딕", sans-serif;">(nullptr);
}
 
inline void FinishMonsterBuffer(
    flatbuffers::FlatBufferBuilder &fbb,
    flatbuffers::Offset</span><mygame::sample::monster><span style="font-family: "맑은 고딕", sans-serif;"> root) {
  fbb.Finish(root);
}
 
// namespace Sample
// namespace MyGame
 
#endif  // FLATBUFFERS_GENERATED_FLATBUFFERS_MYGAME_SAMPLE_H_</span></mygame::sample::monster></mygame::sample::monster></mygame::sample::monster></flatbuffers::offset<weapon></uint8_t></void></flatbuffers::offset<weapon></uint8_t></monster></void></flatbuffers::vector<flatbuffers::offset<weapon></flatbuffers::vector<uint8_t></flatbuffers::string></monster></weapon></int>

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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<tt><span style="font-family: "맑은 고딕", sans-serif;">" ;
 
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_]*</span></tt>

출처 : 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 : 원하는 언어

1
2
3
4
5
6
7
8
9
10
11
12
13
--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파일을 복사 해놓은 위치에 같이 넣어 주세요.


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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream><span style="font-family: "맑은 고딕", sans-serif;">
#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</span><flatbuffers::offset<weapon><span style="font-family: "맑은 고딕", sans-serif;">> 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;
}</span></flatbuffers::offset<weapon></iostream>

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


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


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




반응형
Comments