YAML ain't markup lanuage라고도 불리우며,
사람이 읽을 수도, 이해하기도 쉬운 직렬화 언어라고 생각하면 된다.
데이터 전송 혹은 설정 파일들을 만들 때 주로 사용한다.
XML과 JSON과 비교했을 때, 가장 가독성이 좋다.
가독성이 좋은만큼, 단순히 사용자가 텍스트 파일 수정하듯이 바꿀 수 있다.
이런식으로 바꾸게 될 때, 프로그램이 정상적으로 작동하지 않을 수 있다.
깃허브 사이트 : https://github.com/jbeder/yaml-cpp
공식 사이트 : https://yaml.org/
mkdir OutterProject
git submodule add https://github.com/jbeder/yaml-cpp OutterProject/yaml-cpp
[submodule "third-party/yaml-cpp"]
path = third-party/yaml-cpp
url = https://github.com/jbeder/yaml-cpp
CMAKE_MINIMUM_REQUIRED( VERSION 3.1 )
PROJECT( Example )
ADD_SUBDIRECTORY( third-party/yaml-cpp )
ADD_EXECUTABLE( ${PROJECT_NAME} main.cpp )
INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/yaml-cpp/include )
TARGET_LINK_LIBRARIES( ${PROJECT_NAME} PRIVATE yaml-cpp)
예제가 없다....
Github에 올라와 있는 예제도 다 짤려서 있고,
구글링을 하여도 프로젝트에 yaml-cpp를 추가하는 단계까지만 나와있다.
그래서 나와있는 문법을 노가다하여 그냥 나름의 예제를 만들어 보았다.
monsters.yaml
- name: NewName
position: [0, 5, 0]
powers:
- name: Club
damage: 10
- name: Fist
damage: 8
- name: Dragon
position: [1, 0, 10]
powers:
- name: Fire Breath
damage: 25
- name: Claws
damage: 15
- name: Wizard
position: [5, -3, 0]
powers:
- name: Acid Rain
damage: 50
#include "yaml-cpp/yaml.h"
#include <iostream>
#include <vector>
#include <string>
int main()
{
std::vector<Monster> MonsterData;
// 어? 이 사람 경로 왜 이래? 라고 생각할 수 있다.
// 내 File Tree 는
// Project
// - build
// - Debug
// - Example.exe
// - src
// - main.cpp
// monsters.yaml
// CMakeLists.txt
// .gitsubmodules
// Example.exe 파일 기준으로, monsters.yaml 파일을 찾는 것이기에 이해 바란다.
// 만약, 실행파일과 yaml 파일이 동일한 폴더내에 있으면
// 그냥 monsters.yaml 이라고 하면 된다.
YAML::Node Data = YAML::LoadFile(".\\..\\..\\monsters.yaml");
while(1);
return 0;
}
#include "yaml-cpp/yaml.h"
#include "yaml-cpp/Parser.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
struct VEC3
{
float X;
float Y;
float Z;
};
struct Skill
{
std::string Name;
float Damage;
};
struct Monster
{
std::string Name;
VEC3 Position;
std::vector<Skill> Powers;
};
void GetName( const YAML::Node& node, std::string& Name )
{
// Node에서 정보를 가져올 때는 형변화를 시켜줘야 한다.
Name = node.as<std::string>();
}
void GetPosition( const YAML::Node& node, VEC3& Vec )
{
Vec.X = node[0].as<float>();
Vec.Y = node[1].as<float>();
Vec.Z = node[2].as<float>();
}
void GetDamage( const YAML::Node& node, float& Damage )
{
Damage = node.as<float>();
}
void GetPowers( const YAML::Node& node, std::vector<Skill>& Powers )
{
Skill TempSkill;
for ( auto Temp : node )
{
GetName( Temp["name"], TempSkill.Name );
GetDamage( Temp["damage"], TempSkill.Damage );
Powers.push_back( TempSkill );
}
}
int main()
{
std::vector<Monster> MonsterData;
YAML::Node Data = YAML::LoadFile(".\\..\\..\\monsters.yaml");
for( auto Temp : Data )
{
Monster TempMonster;
// 위의 monsters.yaml 파일을 잘 살펴보자
// - name : NewName
// 이라는 부분이 있다.
// 이러한 부분은 Key : Value로 이루어진 Map이라고 생각하면 된다.
// std::map을 이용할 때처럼, 내가 가져오려고 하는 Value의 Key값을 입력해주면 된다.
// 그리고, yaml은 계층구조를 가지고 있지만, 하위 계층으로 간다고
// 객체의 종류가 바뀌는 것이 아닌, node가 계속 연결된 형식이다.
// 즉, Temp[~~]와 Temp[~~][~~][~~]는 종류가 같다.
std::cout << "Name : " << Temp["name"] << std::endl;
std::cout << "Position : " << Temp["position"][0] << " " << Temp["position"][1] << " " << Temp["position"][2] << " " << std::endl;
GetName( Temp["name"], TempMonster.Name );
GetPosition( Temp["position"], TempMonster.Position );
GetPowers( Temp["powers"], TempMonster.Powers);
MonsterData.push_back( TempMonster );
}
std::cout << " Print Data " << std::endl;
for ( auto Temp : MonsterData )
{
std::cout << "Name : " << Temp.Name << std::endl;
}
while(1);
return 0;
}
#include "yaml-cpp/yaml.h"
#include "yaml-cpp/Parser.h"
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
struct VEC3
{
float X;
float Y;
float Z;
};
struct Skill
{
std::string Name;
float Damage;
};
struct Monster
{
std::string Name;
VEC3 Position;
std::vector<Skill> Powers;
};
void GetName( const YAML::Node& node, std::string& Name )
{
Name = node.as<std::string>();
}
void GetPosition( const YAML::Node& node, VEC3& Vec )
{
Vec.X = node[0].as<float>();
Vec.Y = node[1].as<float>();
Vec.Z = node[2].as<float>();
}
void GetDamage( const YAML::Node& node, float& Damage )
{
Damage = node.as<float>();
}
void GetPowers( const YAML::Node& node, std::vector<Skill>& Powers )
{
Skill TempSkill;
for ( auto Temp : node )
{
GetName( Temp["name"], TempSkill.Name );
GetDamage( Temp["damage"], TempSkill.Damage );
Powers.push_back( TempSkill );
}
}
int main()
{
std::vector<Monster> MonsterData;
YAML::Node Data = YAML::LoadFile(".\\..\\..\\monsters.yaml");
for( auto Temp : Data )
{
Monster TempMonster;
std::cout << "Name : " << Temp["name"] << std::endl;
std::cout << "Position : " << Temp["position"][0] << " " << Temp["position"][1] << " " << Temp["position"][2] << " " << std::endl;
GetName( Temp["name"], TempMonster.Name );
GetPosition( Temp["position"], TempMonster.Position );
GetPowers( Temp["powers"], TempMonster.Powers);
MonsterData.push_back( TempMonster );
}
std::cout << " Print Data " << std::endl;
for ( auto Temp : MonsterData )
{
std::cout << "Name : " << Temp.Name << std::endl;
}
YAML::Emitter Out;
Out << YAML::BeginSeq;
for ( auto Temp : MonsterData )
{
Out << YAML::BeginMap;
Out << YAML::Key << "name" << YAML::Value << Temp.Name;
Out << YAML::Key << "position";
Out << YAML::Flow;
Out << YAML::BeginSeq;
Out << YAML::Value << Temp.Position.X << Temp.Position.Y << Temp.Position.Z << YAML::EndSeq;
Out << YAML::Key << "powers";
Out << YAML::Value;
Out << YAML::BeginSeq;
for ( auto Skills : Temp.Powers )
{
Out << YAML::BeginMap;
Out << YAML::Key << "name" << YAML::Value << Skills.Name;
Out << YAML::Key << "damage" << YAML::Value << Skills.Damage;
Out << YAML::EndMap;
}
Out << YAML::EndSeq;
Out << YAML::EndMap;
}
Out << YAML::EndSeq;
std::ofstream fout(".\\..\\..\\Other.yaml");
fout << Out.c_str();
while(1);
return 0;
}