// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com) // This file is part of the "Nazara Engine - Core module" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include #include #include #include namespace Nz { inline ParameterFile::ParameterFile(Stream& stream) : m_stream(stream) { } template void ParameterFile::Block(Args&&... args) { using Types = TypeListTransform, std::remove_const>, std::decay>; constexpr bool IsOptionalBlock = TypeListHas; if constexpr (IsOptionalBlock) { std::string nextKeyword = ReadKeyword(true); if (nextKeyword != "{") return; } std::string beginToken = ReadKeyword(); if (beginToken != "{") throw std::runtime_error(Format("expected \"{{\" token, got {}", beginToken)); HandleInner("}", std::forward(args)...); std::string endToken = ReadKeyword(); if (endToken != "}") throw std::runtime_error(Format("expected \"}}\" token, got {}", endToken)); } template void ParameterFile::Handle(Args&&... args) { HandleInner({}, std::forward(args)...); } template void ParameterFile::BuildKeyValues(ParameterFile& file, FixedVector& keyValues, K&& key, V&& value, Rest&&... rest) { if constexpr (ShouldIgnore) return BuildKeyValues(file, keyValues, std::forward(value), std::forward(rest)...); else { auto& keyValue = keyValues.emplace_back(); keyValue.key = BuildBlockKey(std::forward(key)); keyValue.handler = BuildBlockHandler(file, std::forward(value)); if constexpr (sizeof...(Rest) > 0) BuildKeyValues(file, keyValues, std::forward(rest)...); } } template auto ParameterFile::GetSingleHandler(ParameterFile& file, V&& value, Rest&&... rest) -> ValueHandler { if constexpr (ShouldIgnore) { static_assert(sizeof...(Rest) > 0, "expected a handler"); return GetSingleHandler(file, std::forward(rest)...); } else { static_assert(sizeof...(Rest) == 0, "expected a single handler"); return BuildBlockHandler(file, std::forward(value)); } } template std::string_view ParameterFile::BuildBlockKey(T&& key) { static_assert(std::is_constructible_v, "parameter key must be convertible to a std::string_view"); return std::string_view(std::forward(key)); } template auto ParameterFile::BuildBlockHandler(ParameterFile& /*file*/, ValueHandler handler) -> ValueHandler { return handler; } template auto ParameterFile::BuildBlockHandler(ParameterFile& /*file*/, T* value) -> ValueHandler { return [value](ParameterFile& file) { *value = ReadValue(file); }; } template auto ParameterFile::BuildBlockHandler(ParameterFile& file, T&& handler, std::enable_if_t>*) -> ValueHandler { using FunctionType = typename FunctionTraits::FuncType; return BuildBlockHandler(file, FunctionRef(handler)); } template auto ParameterFile::BuildBlockHandler(ParameterFile& /*file*/, FunctionRef handler) -> ValueHandler { return [handler](ParameterFile& file) { std::tuple args{ ReadValue(file)... }; std::apply(handler, std::move(args)); }; } template void ParameterFile::HandleInner(std::string_view listEnd, Args&&... args) { using Types = TypeListTransform, std::remove_const>, std::decay>; constexpr bool IsArray = TypeListHas; if constexpr (IsArray) { ValueHandler handler = GetSingleHandler(*this, std::forward(args)...); for (;;) { std::string nextKeyword = ReadKeyword(true); if (nextKeyword == listEnd) break; handler(*this); } } else { FixedVector keys; BuildKeyValues(*this, keys, std::forward(args)...); for (;;) { std::string nextKeyword = ReadKeyword(true); if (nextKeyword == listEnd) break; auto it = std::find_if(keys.begin(), keys.end(), [nextKeyword = ReadKeyword()](const KeyValue& keyValue) { return keyValue.key == nextKeyword; }); if (it == keys.end()) throw std::runtime_error(Format("unexpected keyword \"{}\"", nextKeyword)); const ValueHandler& handler = it->handler; handler(*this); } } } template T ParameterFile::ReadValue(ParameterFile& file) { if constexpr (std::is_same_v) return file.ReadString(); else if constexpr (std::is_same_v) return Keyword{ file.ReadKeyword() }; else static_assert(AlwaysFalse(), "unsupported type"); } } #include