Add rexy::format and rexy::print functions
This commit is contained in:
parent
1bc234cbca
commit
3d17d3ec8c
105
include/rexy/detail/format/arg_store.hpp
Normal file
105
include/rexy/detail/format/arg_store.hpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_ARG_STORE_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_ARG_STORE_HPP
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include "basic_types.hpp" //arg_info
|
||||||
|
#include "storage.hpp" //stored_type
|
||||||
|
#include "named_args.hpp" //count_named_args
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
//Implementation of argument storage, erasing the need of passing around a parameter pack. Used in
|
||||||
|
//constructor of basic_format_args
|
||||||
|
template<class Context, class... Args>
|
||||||
|
class basic_format_arg_store
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using char_type = typename Context::char_type;
|
||||||
|
public:
|
||||||
|
static constexpr std::size_t num_args = sizeof...(Args);
|
||||||
|
static constexpr std::size_t num_named_args = count_named_args_v<Args...>;
|
||||||
|
|
||||||
|
struct format_data
|
||||||
|
{
|
||||||
|
friend class basic_format_arg_store;
|
||||||
|
private:
|
||||||
|
unsigned char m_data[
|
||||||
|
(sizeof(arg_info) * num_args) +
|
||||||
|
(sizeof(basic_string_view<char_type>) * num_named_args) +
|
||||||
|
(sizeof(stored_type_t<Args,Context>) + ...)
|
||||||
|
] = {};
|
||||||
|
|
||||||
|
arg_info* info(void){
|
||||||
|
return reinterpret_cast<arg_info*>(raw_info());
|
||||||
|
}
|
||||||
|
constexpr unsigned char* data(void){
|
||||||
|
return raw_info() + (sizeof(arg_info) * num_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr unsigned char* raw_info(void){
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
constexpr const unsigned char* raw_info(void)const{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const arg_info* info(void)const{
|
||||||
|
return reinterpret_cast<const arg_info*>(raw_info());
|
||||||
|
}
|
||||||
|
const unsigned char* data(void)const{
|
||||||
|
return raw_info() + (sizeof(arg_info) * num_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned char* raw(void)const{
|
||||||
|
return m_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}packed_data;
|
||||||
|
|
||||||
|
basic_format_arg_store(Args&&... args);
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class Arg>
|
||||||
|
void store_arg(std::size_t argnum, Arg&& arg);
|
||||||
|
template<NamedArg Arg>
|
||||||
|
void store_arg(std::size_t argnum, Arg&& arg);
|
||||||
|
|
||||||
|
template<class... SArgs>
|
||||||
|
void store_args(SArgs&&... args);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Specialized for empty parameter pack
|
||||||
|
template<class Context>
|
||||||
|
class basic_format_arg_store<Context>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr std::size_t num_args = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
89
include/rexy/detail/format/arg_store.tpp
Normal file
89
include/rexy/detail/format/arg_store.tpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_ARG_STORE_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_ARG_STORE_TPP
|
||||||
|
|
||||||
|
#include "arg_store.hpp"
|
||||||
|
|
||||||
|
#include "storage.hpp"
|
||||||
|
#include "basic_types.hpp"
|
||||||
|
|
||||||
|
#include "../../utility.hpp" //memcpy
|
||||||
|
|
||||||
|
#include <utility> //forward
|
||||||
|
#include <memory> //addressof
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
///////////////////////basic_format_arg_store////////////////////////
|
||||||
|
template<class Context, class... Args>
|
||||||
|
basic_format_arg_store<Context,Args...>::basic_format_arg_store(Args&&... args){
|
||||||
|
store_args(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
template<class Context, class... Args>
|
||||||
|
template<class Arg>
|
||||||
|
void basic_format_arg_store<Context,Args...>::store_arg(std::size_t argnum, Arg&& arg){
|
||||||
|
using stored_type = stored_type_t<Arg,Context>;
|
||||||
|
arg_info& ai = packed_data.info()[argnum];
|
||||||
|
ai.type = map_to_storage_enum_v<stored_type,typename Context::char_type>;
|
||||||
|
|
||||||
|
//convert to one of the storable types
|
||||||
|
stored_type st{std::forward<Arg>(arg)};
|
||||||
|
|
||||||
|
//save to the array of data
|
||||||
|
rexy::memcpy(packed_data.data()+ai.offset, std::addressof(st), sizeof(st));
|
||||||
|
|
||||||
|
//setup next entry's offset
|
||||||
|
if(argnum+1 < num_args){
|
||||||
|
packed_data.info()[argnum+1].offset = ai.offset + sizeof(st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<class Context, class... Args>
|
||||||
|
template<NamedArg Arg>
|
||||||
|
void basic_format_arg_store<Context,Args...>::store_arg(std::size_t argnum, Arg&& arg){
|
||||||
|
using stored_type = stored_type_t<Arg,Context>;
|
||||||
|
arg_info& ai = packed_data.info()[argnum];
|
||||||
|
ai.type = map_to_storage_enum_v<stored_type,typename Context::char_type>;
|
||||||
|
ai.named = true;
|
||||||
|
const std::size_t name_size = sizeof(format_string_view<char_type>);
|
||||||
|
|
||||||
|
//convert to one of the storable types
|
||||||
|
stored_type st{std::forward<Arg>(arg).value};
|
||||||
|
format_string_view<char_type> name{arg.name.c_str(), arg.name.length()};
|
||||||
|
|
||||||
|
//save to the array of data
|
||||||
|
rexy::memcpy(packed_data.data()+ai.offset, std::addressof(name), name_size);
|
||||||
|
rexy::memcpy(packed_data.data()+ai.offset+name_size, std::addressof(st), sizeof(st));
|
||||||
|
|
||||||
|
//setup next entry's offset
|
||||||
|
if(argnum+1 < num_args){
|
||||||
|
packed_data.info()[argnum+1].offset = ai.offset + sizeof(st) + name_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<class Context, class... Args>
|
||||||
|
template<class... SArgs>
|
||||||
|
void basic_format_arg_store<Context,Args...>::store_args(SArgs&&... args){
|
||||||
|
std::size_t argnum = 0;
|
||||||
|
(store_arg(argnum++, std::forward<SArgs>(args)), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
100
include/rexy/detail/format/basic_types.hpp
Normal file
100
include/rexy/detail/format/basic_types.hpp
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_BASIC_TYPES_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_BASIC_TYPES_HPP
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
|
#include "storage.hpp"
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
static constexpr std::size_t invalid_arg_index = std::size_t{-1};
|
||||||
|
|
||||||
|
|
||||||
|
template<class Char>
|
||||||
|
struct format_string_view{
|
||||||
|
const Char* name;
|
||||||
|
std::size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct arg_info{
|
||||||
|
storage_type type = storage_type::none_t;
|
||||||
|
std::size_t offset = 0;
|
||||||
|
bool named = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class alignment : int{
|
||||||
|
none = 0,
|
||||||
|
left = '<',
|
||||||
|
right = '>',
|
||||||
|
center = '^'
|
||||||
|
};
|
||||||
|
enum class presentation{
|
||||||
|
default_t,
|
||||||
|
int_t,
|
||||||
|
char_t,
|
||||||
|
float_t,
|
||||||
|
string_t,
|
||||||
|
ptr_t
|
||||||
|
};
|
||||||
|
|
||||||
|
struct format_specs{
|
||||||
|
int width = 0;
|
||||||
|
int precision = 0;
|
||||||
|
int type = 0;
|
||||||
|
alignment align = alignment::none;
|
||||||
|
presentation present = presentation::default_t;
|
||||||
|
int align_char = ' ';
|
||||||
|
int sign = 0;
|
||||||
|
bool locale = false;
|
||||||
|
bool alt_form = false;
|
||||||
|
bool zero_fill = false;
|
||||||
|
};
|
||||||
|
enum class dyn_type{
|
||||||
|
none = 0,
|
||||||
|
index,
|
||||||
|
string
|
||||||
|
};
|
||||||
|
template<class Char>
|
||||||
|
struct dynamic_format_specs : public format_specs{
|
||||||
|
union dyn_val{
|
||||||
|
std::size_t index = invalid_arg_index;
|
||||||
|
format_string_view<Char> name;
|
||||||
|
}dyn_width, dyn_precision;
|
||||||
|
|
||||||
|
dyn_type width_type = dyn_type::index;
|
||||||
|
dyn_type precision_type = dyn_type::index;
|
||||||
|
};
|
||||||
|
template<class FormatCtx>
|
||||||
|
constexpr void normalize_dynamic_format_specs(FormatCtx& ctx, dynamic_format_specs<typename FormatCtx::char_type>& specs);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace rexy{
|
||||||
|
|
||||||
|
template<class OutIt>
|
||||||
|
struct format_to_n_result{
|
||||||
|
OutIt out;
|
||||||
|
std::size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
55
include/rexy/detail/format/basic_types.tpp
Normal file
55
include/rexy/detail/format/basic_types.tpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_BASIC_TYPES_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_BASIC_TYPES_TPP
|
||||||
|
|
||||||
|
#include "basic_types.hpp"
|
||||||
|
|
||||||
|
#include "format_args.hpp"
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
template<class FormatCtx>
|
||||||
|
constexpr void normalize_dynamic_format_specs(FormatCtx& ctx, dynamic_format_specs<typename FormatCtx::char_type>& specs){
|
||||||
|
std::size_t index = 0;
|
||||||
|
if(specs.width_type == dyn_type::index){
|
||||||
|
index = specs.dyn_width.index;
|
||||||
|
}else{
|
||||||
|
const auto& name = specs.dyn_width.name;
|
||||||
|
index = ctx.arg_index(name.name, name.name + name.length);
|
||||||
|
}
|
||||||
|
specs.width = ((index != invalid_arg_index)
|
||||||
|
? visit_format_arg(parse::dynamic_integer_retriever{}, ctx.arg(index))
|
||||||
|
: specs.width);
|
||||||
|
if(specs.precision_type == dyn_type::index){
|
||||||
|
index = specs.dyn_precision.index;
|
||||||
|
}else{
|
||||||
|
const auto& name = specs.dyn_precision.name;
|
||||||
|
index = ctx.arg_index(name.name, name.name + name.length);
|
||||||
|
}
|
||||||
|
specs.precision = ((index != invalid_arg_index)
|
||||||
|
? visit_format_arg(parse::dynamic_integer_retriever{}, ctx.arg(index))
|
||||||
|
: specs.precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
97
include/rexy/detail/format/context_handler.hpp
Normal file
97
include/rexy/detail/format/context_handler.hpp
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_CONTEXT_HANDLER_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_CONTEXT_HANDLER_HPP
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
|
#include "standard_types.hpp"
|
||||||
|
#include "internal_types.hpp"
|
||||||
|
#include "format_context.hpp"
|
||||||
|
#include "parse_context.hpp"
|
||||||
|
#include "format_args.hpp"
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
|
#include <locale> //locale
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
//Holds the format and parse contexts and performs parse+format calls for replacement fields
|
||||||
|
template<class Char>
|
||||||
|
struct format_handler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using char_type = Char;
|
||||||
|
using fmt_ctx_t = fmt_context_t<char_type>;
|
||||||
|
using parse_ctx_t = parse_context_t<char_type>;
|
||||||
|
using out_it_t = output_iterator_t<char_type>;
|
||||||
|
using fmt_it_t = typename parse_ctx_t::iterator;
|
||||||
|
using handle_t = typename basic_format_arg<fmt_ctx_t>::handle;
|
||||||
|
template<class T>
|
||||||
|
using fmter_t = typename fmt_ctx_t::template formatter_type<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
fmt_ctx_t fmt_ctx;
|
||||||
|
parse_ctx_t parse_ctx;
|
||||||
|
|
||||||
|
public:
|
||||||
|
format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args);
|
||||||
|
format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args, const std::locale& l);
|
||||||
|
constexpr fmt_it_t do_raw(fmt_it_t start, fmt_it_t fin);
|
||||||
|
constexpr fmt_it_t do_format_spec(fmt_it_t start, fmt_it_t fin, std::size_t id);
|
||||||
|
constexpr fmt_it_t do_empty_format(fmt_it_t start, fmt_it_t last, std::size_t id);
|
||||||
|
|
||||||
|
constexpr std::size_t on_arg_id(void);
|
||||||
|
constexpr std::size_t on_arg_id(std::size_t i);
|
||||||
|
constexpr std::size_t on_arg_id(fmt_it_t start, fmt_it_t last);
|
||||||
|
};
|
||||||
|
template<class Char>
|
||||||
|
format_handler(output_iterator_t<Char>, basic_string_view<Char>, basic_format_args<fmt_context_t<Char>>) -> format_handler<Char>;
|
||||||
|
|
||||||
|
//Constant-evaluated version of format_handler. Does not perform formatting but will complete a parsing
|
||||||
|
//pass during compile
|
||||||
|
template<class Char, class... Args>
|
||||||
|
class format_checker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using char_type = Char;
|
||||||
|
using parse_ctx_t = parse_context_t<char_type>;
|
||||||
|
using fmt_it_t = typename parse_ctx_t::iterator;
|
||||||
|
|
||||||
|
parse_ctx_t parse_ctx;
|
||||||
|
|
||||||
|
consteval format_checker(void) = default;
|
||||||
|
consteval format_checker(basic_string_view<char_type> fmt, std::size_t numargs = 0);
|
||||||
|
constexpr fmt_it_t do_raw(fmt_it_t, fmt_it_t last);
|
||||||
|
constexpr fmt_it_t do_format_spec(fmt_it_t start, fmt_it_t last, std::size_t id);
|
||||||
|
constexpr void do_empty_format(fmt_it_t, fmt_it_t, std::size_t);
|
||||||
|
|
||||||
|
constexpr std::size_t on_arg_id(void);
|
||||||
|
constexpr std::size_t on_arg_id(std::size_t i);
|
||||||
|
constexpr std::size_t on_arg_id(fmt_it_t start, fmt_it_t last);
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<std::size_t I, class FArg, class... FArgs>
|
||||||
|
constexpr fmt_it_t do_format_spec_impl(fmt_it_t start, fmt_it_t last, std::size_t id);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
162
include/rexy/detail/format/context_handler.tpp
Normal file
162
include/rexy/detail/format/context_handler.tpp
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_CONTEXT_HANDLER_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_CONTEXT_HANDLER_TPP
|
||||||
|
|
||||||
|
#include "context_handler.hpp"
|
||||||
|
|
||||||
|
#include "parse_context.hpp"
|
||||||
|
#include "format_context.hpp"
|
||||||
|
#include "formatter.hpp"
|
||||||
|
#include "format_args.hpp"
|
||||||
|
#include "named_args.hpp"
|
||||||
|
|
||||||
|
#include "format_error.hpp"
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
|
#include <locale> //locale
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include <type_traits> //remove_cvref
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
///////////////////////////////format_handler//////////////////////////////////
|
||||||
|
template<class Char>
|
||||||
|
format_handler<Char>::format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args):
|
||||||
|
fmt_ctx(args, output),
|
||||||
|
parse_ctx(fmt){}
|
||||||
|
template<class Char>
|
||||||
|
format_handler<Char>::format_handler(out_it_t output, basic_string_view<char_type> fmt, basic_format_args<fmt_ctx_t> args, const std::locale& l):
|
||||||
|
fmt_ctx(args, output, l),
|
||||||
|
parse_ctx(fmt){}
|
||||||
|
|
||||||
|
template<class Char>
|
||||||
|
constexpr auto format_handler<Char>::do_raw(fmt_it_t start, fmt_it_t fin) -> fmt_it_t{
|
||||||
|
for(auto it = start;it != fin;++it){
|
||||||
|
fmt_ctx.out() = *it;
|
||||||
|
}
|
||||||
|
return fin;
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
constexpr auto format_handler<Char>::do_format_spec(fmt_it_t start, fmt_it_t fin, std::size_t id) -> fmt_it_t{
|
||||||
|
parse_ctx.advance_to(start);
|
||||||
|
const auto arg = fmt_ctx.arg(id);
|
||||||
|
format_specs specs;
|
||||||
|
visit_format_arg(format::arg_formatter<fmt_ctx_t,parse_ctx_t,format_specs>{fmt_ctx, parse_ctx, specs}, arg);
|
||||||
|
return parse_ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char>
|
||||||
|
constexpr auto format_handler<Char>::do_empty_format(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
|
||||||
|
parse_ctx.advance_to(start);
|
||||||
|
const auto arg = fmt_ctx.arg(id);
|
||||||
|
visit_format_arg(format::empty_formatter<fmt_ctx_t,parse_ctx_t>{fmt_ctx, parse_ctx}, arg);
|
||||||
|
return parse_ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char>
|
||||||
|
constexpr std::size_t format_handler<Char>::on_arg_id(void){
|
||||||
|
return parse_ctx.next_arg_id();
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
constexpr std::size_t format_handler<Char>::on_arg_id(std::size_t i){
|
||||||
|
parse_ctx.check_arg_id(i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
constexpr std::size_t format_handler<Char>::on_arg_id(fmt_it_t start, fmt_it_t last){
|
||||||
|
const auto id = fmt_ctx.arg_index(start, last);
|
||||||
|
parse_ctx.check_arg_id(id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////format_checker//////////////////////////////////
|
||||||
|
template<class Char, class... Args>
|
||||||
|
consteval format_checker<Char,Args...>::format_checker(basic_string_view<char_type> fmt, std::size_t numargs):
|
||||||
|
parse_ctx(fmt, numargs){}
|
||||||
|
template<class Char, class... Args>
|
||||||
|
constexpr auto format_checker<Char,Args...>::do_raw(fmt_it_t, fmt_it_t last) -> fmt_it_t{
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
template<class Char, class... Args>
|
||||||
|
constexpr auto format_checker<Char,Args...>::do_format_spec(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
|
||||||
|
parse_ctx.advance_to(start);
|
||||||
|
if constexpr(sizeof...(Args)){
|
||||||
|
return do_format_spec_impl<0,Args...>(start, last, id);
|
||||||
|
}else{
|
||||||
|
REXY_THROW_FORMAT_ERROR("Missing argument");
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<class Char, class... Args>
|
||||||
|
constexpr void format_checker<Char,Args...>::do_empty_format(fmt_it_t, fmt_it_t, std::size_t){/*nothing to parse and the checker doesn't format so just return*/}
|
||||||
|
template<class Char, class... Args>
|
||||||
|
template<std::size_t I, class FArg, class... FArgs>
|
||||||
|
constexpr auto format_checker<Char,Args...>::do_format_spec_impl(fmt_it_t start, fmt_it_t last, std::size_t id) -> fmt_it_t{
|
||||||
|
if(I == id){
|
||||||
|
using fmt_ctx_t = fmt_context_t<Char>;
|
||||||
|
using base_type = std::remove_cvref_t<FArg>;
|
||||||
|
using stored_type = stored_type_t<base_type,fmt_ctx_t>;
|
||||||
|
if constexpr(Handle<stored_type,fmt_ctx_t>){
|
||||||
|
using fmter_t = typename fmt_ctx_t::template formatter_type<base_type>;
|
||||||
|
fmter_t fmter{};
|
||||||
|
return fmter.parse(parse_ctx);
|
||||||
|
}else{
|
||||||
|
dynamic_format_specs<Char> specs{};
|
||||||
|
format_specs_checker<cx_format_specs_handler<parse_ctx_t,Args...>> handler{
|
||||||
|
cx_format_specs_handler<parse_ctx_t,Args...>{parse_ctx, specs},
|
||||||
|
detail::map_to_storage_enum_v<stored_type,Char>
|
||||||
|
};
|
||||||
|
return parse::perform_standard_parse(start, last, handler);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if constexpr(sizeof...(FArgs) > 0){
|
||||||
|
return do_format_spec_impl<I+1,FArgs...>(start, last, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
REXY_THROW_FORMAT_ERROR("Missing argument");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class... Args>
|
||||||
|
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(void){
|
||||||
|
return parse_ctx.next_arg_id();
|
||||||
|
}
|
||||||
|
template<class Char, class... Args>
|
||||||
|
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(std::size_t i){
|
||||||
|
if(i > sizeof...(Args)){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Arg index out of bounds");
|
||||||
|
}
|
||||||
|
parse_ctx.check_arg_id(i);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
template<class Char, class... Args>
|
||||||
|
constexpr std::size_t format_checker<Char,Args...>::on_arg_id(fmt_it_t start, fmt_it_t last){
|
||||||
|
const std::size_t id = find_static_named_arg_id<fmt_it_t, Args...>(start, last);
|
||||||
|
if(id == invalid_arg_index){
|
||||||
|
REXY_THROW_FORMAT_ERROR("No such named arg");
|
||||||
|
}
|
||||||
|
return on_arg_id(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
130
include/rexy/detail/format/format_args.hpp
Normal file
130
include/rexy/detail/format/format_args.hpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_FORMAT_ARGS_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_FORMAT_ARGS_HPP
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include <variant> //monostate
|
||||||
|
|
||||||
|
#include "storage.hpp" //storage_type
|
||||||
|
#include "basic_types.hpp" //arg_info
|
||||||
|
#include "arg_store.hpp"
|
||||||
|
#include "traits.hpp"
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
|
namespace rexy::fmt{
|
||||||
|
|
||||||
|
//Type erasure of single argument.
|
||||||
|
//Context is an instantiation of 'basic_format_context'
|
||||||
|
template<class Context>
|
||||||
|
class basic_format_arg
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using char_type = typename Context::char_type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
//Handles call to formatter for a user defined type
|
||||||
|
//type erase the user defined values
|
||||||
|
class handle
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using format_ptr_t = void(handle::*)(basic_format_parse_context<char_type>&, Context&)const;
|
||||||
|
public:
|
||||||
|
const void* m_data;
|
||||||
|
format_ptr_t m_format_impl;
|
||||||
|
public:
|
||||||
|
template<class T>
|
||||||
|
explicit handle(T&& t);
|
||||||
|
template<class T>
|
||||||
|
void format_impl(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const;
|
||||||
|
void format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const;
|
||||||
|
};
|
||||||
|
|
||||||
|
union{
|
||||||
|
std::monostate none_v = {};
|
||||||
|
bool bool_v;
|
||||||
|
char_type char_v;
|
||||||
|
int int_v;
|
||||||
|
unsigned int uint_v;
|
||||||
|
long long long_long_v;
|
||||||
|
unsigned long long ulong_long_v;
|
||||||
|
float float_v;
|
||||||
|
double double_v;
|
||||||
|
long double long_double_v;
|
||||||
|
const char_type* char_ptr_v;
|
||||||
|
basic_string_view<char_type> string_v;
|
||||||
|
const void* ptr_v;
|
||||||
|
handle custom_v;
|
||||||
|
};
|
||||||
|
detail::storage_type active_member = detail::storage_type::none_t;
|
||||||
|
|
||||||
|
//initialize std::variant equivalent to std::monostate
|
||||||
|
basic_format_arg(void)noexcept{}
|
||||||
|
|
||||||
|
explicit basic_format_arg(bool b)noexcept;
|
||||||
|
explicit basic_format_arg(char_type b)noexcept;
|
||||||
|
explicit basic_format_arg(int b)noexcept;
|
||||||
|
explicit basic_format_arg(unsigned int b)noexcept;
|
||||||
|
explicit basic_format_arg(long long b)noexcept;
|
||||||
|
explicit basic_format_arg(unsigned long long b)noexcept;
|
||||||
|
explicit basic_format_arg(float b)noexcept;
|
||||||
|
explicit basic_format_arg(double b)noexcept;
|
||||||
|
explicit basic_format_arg(long double b)noexcept;
|
||||||
|
explicit basic_format_arg(const char_type* b)noexcept;
|
||||||
|
explicit basic_format_arg(basic_string_view<char_type> b)noexcept;
|
||||||
|
explicit basic_format_arg(const void* b)noexcept;
|
||||||
|
explicit basic_format_arg(const handle b)noexcept;
|
||||||
|
|
||||||
|
detail::storage_type type(void)const;
|
||||||
|
|
||||||
|
explicit operator bool(void)const noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Holds list of arguments, erasing concrete types.
|
||||||
|
//Context is an instantiation of 'basic_format_context'
|
||||||
|
template<class Context>
|
||||||
|
class basic_format_args
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using char_type = detail::extract_char_type_from_context_t<Context>;
|
||||||
|
private:
|
||||||
|
//TODO less pointers
|
||||||
|
const detail::arg_info* m_info = nullptr;
|
||||||
|
const unsigned char* m_data = nullptr;
|
||||||
|
const std::size_t m_num_args = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
basic_format_args(void)noexcept = default;
|
||||||
|
|
||||||
|
constexpr basic_format_args(const detail::basic_format_arg_store<Context>& store)noexcept;
|
||||||
|
template<class... Args>
|
||||||
|
basic_format_args(const detail::basic_format_arg_store<Context,Args...>& store)noexcept;
|
||||||
|
|
||||||
|
basic_format_arg<Context> get(std::size_t i)const noexcept;
|
||||||
|
basic_format_arg<Context> get(const char_type* first, const char_type* last)const;
|
||||||
|
std::size_t get_index(const char_type* first, const char_type* last)const;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Visitor for basic_format_arg
|
||||||
|
template<class Fun, class Context>
|
||||||
|
decltype(auto) visit_format_arg(Fun&& fun, const basic_format_arg<Context>& arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
236
include/rexy/detail/format/format_args.tpp
Normal file
236
include/rexy/detail/format/format_args.tpp
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_FORMAT_ARGS_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_FORMAT_ARGS_TPP
|
||||||
|
|
||||||
|
#include "format_args.hpp"
|
||||||
|
|
||||||
|
#include "basic_types.hpp" //format_string_view
|
||||||
|
#include "standard_types.hpp"
|
||||||
|
#include "parse_context.hpp"
|
||||||
|
#include "format_context.hpp"
|
||||||
|
#include "storage.hpp"
|
||||||
|
#include "arg_store.hpp"
|
||||||
|
#include "format_error.hpp"
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
|
#include <utility> //forward
|
||||||
|
#include <type_traits> //remove_cvref, add_lvalue_reference, add_pointer, add_const, conditional
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include <memory> //addressof
|
||||||
|
|
||||||
|
namespace rexy::fmt{
|
||||||
|
|
||||||
|
/////////////////////////////basic_format_arg::handle//////////////////////////////
|
||||||
|
template<class Context>
|
||||||
|
template<class T>
|
||||||
|
basic_format_arg<Context>::handle::handle(T&& t):
|
||||||
|
m_data(std::addressof(t)),
|
||||||
|
m_format_impl(&handle::format_impl<std::remove_cvref_t<T>>){}
|
||||||
|
template<class Context>
|
||||||
|
template<class T>
|
||||||
|
void basic_format_arg<Context>::handle::format_impl(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const{
|
||||||
|
using value_type = std::remove_cvref_t<T>;
|
||||||
|
using formatter_t = typename Context::template formatter_type<value_type>;
|
||||||
|
using qualified_type = std::conditional_t<detail::has_const_formatter<value_type,Context>,
|
||||||
|
std::add_const_t<value_type>,
|
||||||
|
value_type>;
|
||||||
|
using reference_type = std::add_lvalue_reference_t<qualified_type>;
|
||||||
|
using pointer_type = std::add_pointer_t<qualified_type>;
|
||||||
|
using const_pointer_type = std::add_pointer_t<std::add_const_t<qualified_type>>;
|
||||||
|
|
||||||
|
reference_type ref = *const_cast<pointer_type>(reinterpret_cast<const_pointer_type>(m_data));
|
||||||
|
|
||||||
|
formatter_t fmt;
|
||||||
|
parse_ctx.advance_to(fmt.parse(parse_ctx));
|
||||||
|
format_ctx.advance_to(fmt.format(ref, format_ctx));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Context>
|
||||||
|
void basic_format_arg<Context>::handle::format(basic_format_parse_context<char_type>& parse_ctx, Context& format_ctx)const{
|
||||||
|
//Pretty epic gamer moment here
|
||||||
|
(this->*m_format_impl)(parse_ctx, format_ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////basic_format_arg////////////////////////////////
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(bool b)noexcept:
|
||||||
|
bool_v(b),
|
||||||
|
active_member(detail::storage_type::bool_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(char_type b)noexcept:
|
||||||
|
char_v(b),
|
||||||
|
active_member(detail::storage_type::char_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(int b)noexcept:
|
||||||
|
int_v(b),
|
||||||
|
active_member(detail::storage_type::int_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(unsigned int b)noexcept:
|
||||||
|
uint_v(b),
|
||||||
|
active_member(detail::storage_type::uint_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(long long b)noexcept:
|
||||||
|
long_long_v(b),
|
||||||
|
active_member(detail::storage_type::long_long_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(unsigned long long b)noexcept:
|
||||||
|
ulong_long_v(b),
|
||||||
|
active_member(detail::storage_type::ulong_long_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(float b)noexcept:
|
||||||
|
float_v(b),
|
||||||
|
active_member(detail::storage_type::float_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(double b)noexcept:
|
||||||
|
double_v(b),
|
||||||
|
active_member(detail::storage_type::double_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(long double b)noexcept:
|
||||||
|
long_double_v(b),
|
||||||
|
active_member(detail::storage_type::long_double_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(const char_type* b)noexcept:
|
||||||
|
char_ptr_v(b),
|
||||||
|
active_member(detail::storage_type::char_ptr_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(basic_string_view<char_type> b)noexcept:
|
||||||
|
string_v(b),
|
||||||
|
active_member(detail::storage_type::string_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(const void* b)noexcept:
|
||||||
|
ptr_v(b),
|
||||||
|
active_member(detail::storage_type::ptr_t){}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::basic_format_arg(const handle b)noexcept:
|
||||||
|
custom_v(b),
|
||||||
|
active_member(detail::storage_type::custom_t){}
|
||||||
|
template<class Context>
|
||||||
|
detail::storage_type basic_format_arg<Context>::type(void)const{
|
||||||
|
return active_member;
|
||||||
|
}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context>::operator bool(void)const noexcept{
|
||||||
|
return active_member != detail::storage_type::none_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////basic_format_args////////////////////////////////
|
||||||
|
template<class Context>
|
||||||
|
constexpr basic_format_args<Context>::basic_format_args(const detail::basic_format_arg_store<Context>&)noexcept{}
|
||||||
|
template<class Context>
|
||||||
|
template<class... Args>
|
||||||
|
basic_format_args<Context>::basic_format_args(const detail::basic_format_arg_store<Context,Args...>& store)noexcept:
|
||||||
|
m_info(store.packed_data.info()),
|
||||||
|
m_data(store.packed_data.data()),
|
||||||
|
m_num_args(sizeof...(Args)){}
|
||||||
|
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context> basic_format_args<Context>::get(std::size_t i)const noexcept{
|
||||||
|
const bool named = m_info[i].named;
|
||||||
|
const std::size_t offset = m_info[i].offset + (named ? sizeof(detail::format_string_view<char_type>) : 0);
|
||||||
|
const unsigned char* const data = m_data + offset;
|
||||||
|
|
||||||
|
switch(m_info[i].type){
|
||||||
|
case detail::storage_type::bool_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const bool*>(data)};
|
||||||
|
case detail::storage_type::char_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const char_type*>(data)};
|
||||||
|
case detail::storage_type::int_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const int*>(data)};
|
||||||
|
case detail::storage_type::uint_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const unsigned int*>(data)};
|
||||||
|
case detail::storage_type::long_long_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const long long*>(data)};
|
||||||
|
case detail::storage_type::ulong_long_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const unsigned long long*>(data)};
|
||||||
|
case detail::storage_type::float_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const float*>(data)};
|
||||||
|
case detail::storage_type::double_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const double*>(data)};
|
||||||
|
case detail::storage_type::long_double_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const long double*>(data)};
|
||||||
|
case detail::storage_type::char_ptr_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const char_type* const*>(data)};
|
||||||
|
case detail::storage_type::string_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const basic_string_view<char_type>*>(data)};
|
||||||
|
case detail::storage_type::ptr_t:
|
||||||
|
return basic_format_arg<Context>{reinterpret_cast<const void*>(*reinterpret_cast<const char* const*>(data))};
|
||||||
|
case detail::storage_type::custom_t:
|
||||||
|
return basic_format_arg<Context>{*reinterpret_cast<const typename basic_format_arg<Context>::handle*>(data)};
|
||||||
|
default:
|
||||||
|
return basic_format_arg<Context>{};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
template<class Context>
|
||||||
|
basic_format_arg<Context> basic_format_args<Context>::get(const char_type* first, const char_type* last)const{
|
||||||
|
return get(get_index(first, last));
|
||||||
|
}
|
||||||
|
template<class Context>
|
||||||
|
std::size_t basic_format_args<Context>::get_index(const char_type* first, const char_type* last)const{
|
||||||
|
for(std::size_t i = 0;i < m_num_args;++i){
|
||||||
|
if(m_info[i].named){
|
||||||
|
auto data = reinterpret_cast<const detail::format_string_view<char_type>* const>(m_data + m_info[i].offset);
|
||||||
|
const std::size_t len = last - first;
|
||||||
|
if(len == data->length && !rexy::strncmp(data->name, first, len)){
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
REXY_THROW_FORMAT_ERROR("Requested named arg does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Fun, class Context>
|
||||||
|
decltype(auto) visit_format_arg(Fun&& fun, const basic_format_arg<Context>& arg){
|
||||||
|
switch(arg.type()){
|
||||||
|
case detail::storage_type::bool_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.bool_v);
|
||||||
|
case detail::storage_type::char_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.char_v);
|
||||||
|
case detail::storage_type::int_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.int_v);
|
||||||
|
case detail::storage_type::uint_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.uint_v);
|
||||||
|
case detail::storage_type::long_long_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.long_long_v);
|
||||||
|
case detail::storage_type::ulong_long_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.ulong_long_v);
|
||||||
|
case detail::storage_type::float_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.float_v);
|
||||||
|
case detail::storage_type::double_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.double_v);
|
||||||
|
case detail::storage_type::long_double_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.long_double_v);
|
||||||
|
case detail::storage_type::char_ptr_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.char_ptr_v);
|
||||||
|
case detail::storage_type::string_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.string_v);
|
||||||
|
case detail::storage_type::ptr_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.ptr_v);
|
||||||
|
case detail::storage_type::custom_t:
|
||||||
|
return std::forward<Fun>(fun)(arg.custom_v);
|
||||||
|
default:
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid format_arg storage_type");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
77
include/rexy/detail/format/format_context.hpp
Normal file
77
include/rexy/detail/format/format_context.hpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_FORMAT_CONTEXT_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_FORMAT_CONTEXT_HPP
|
||||||
|
|
||||||
|
#include <type_traits> //remove_cvref, declval, integral_constant, void_t
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include <locale> //locale
|
||||||
|
|
||||||
|
#include "standard_types.hpp" //basic_format_args, formatter
|
||||||
|
|
||||||
|
namespace rexy::fmt{
|
||||||
|
|
||||||
|
namespace detail{
|
||||||
|
|
||||||
|
//type trait used for properly cv-qualifying the argument to a call to formatter<type>::format
|
||||||
|
template<class T, class Context, class = void>
|
||||||
|
struct is_formatter_const : public std::false_type{};
|
||||||
|
template<class T, class Context>
|
||||||
|
struct is_formatter_const<T,Context,std::void_t<
|
||||||
|
decltype(std::declval<typename Context::template formatter_type<std::remove_cvref_t<T>>>().format(std::declval<const T&>(), std::declval<Context>()))
|
||||||
|
>> : public std::true_type{};
|
||||||
|
template<class T, class Context>
|
||||||
|
concept has_formatter = requires(typename Context::template formatter_type<std::remove_cvref_t<T>> fmter, T& t, Context& ctx){
|
||||||
|
fmter.format(t, ctx);
|
||||||
|
};
|
||||||
|
template<class T, class Context>
|
||||||
|
concept has_const_formatter = has_formatter<const std::remove_cvref_t<T>, Context>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Holds the arguments passed to calls to 'format' or 'vformat' in type erasing structure.
|
||||||
|
//Additionally holds the iterator where output is written.
|
||||||
|
template<class OutIt, class Char>
|
||||||
|
class basic_format_context
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using iterator = OutIt;
|
||||||
|
using char_type = Char;
|
||||||
|
template<class T>
|
||||||
|
using formatter_type = formatter<T, Char>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
basic_format_args<basic_format_context> m_fmt_args;
|
||||||
|
iterator m_outit;
|
||||||
|
std::locale m_locale;
|
||||||
|
|
||||||
|
public:
|
||||||
|
basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit);
|
||||||
|
basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit, const std::locale& l);
|
||||||
|
basic_format_arg<basic_format_context> arg(std::size_t id)const;
|
||||||
|
basic_format_arg<basic_format_context> arg(const char_type* first, const char_type* last)const;
|
||||||
|
std::size_t arg_index(const char_type* first, const char_type* last)const;
|
||||||
|
std::locale locale(void);
|
||||||
|
iterator out(void);
|
||||||
|
void advance_to(iterator it);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
70
include/rexy/detail/format/format_context.tpp
Normal file
70
include/rexy/detail/format/format_context.tpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_FORMAT_CONTEXT_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_FORMAT_CONTEXT_TPP
|
||||||
|
|
||||||
|
#include "format_context.hpp"
|
||||||
|
|
||||||
|
#include "format_args.hpp"
|
||||||
|
|
||||||
|
#include <utility> //move
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include <locale> //locale
|
||||||
|
|
||||||
|
namespace rexy::fmt{
|
||||||
|
|
||||||
|
///////////////////////////////basic_format_context////////////////////////////////
|
||||||
|
template<class OutIt, class Char>
|
||||||
|
basic_format_context<OutIt,Char>::basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit):
|
||||||
|
m_fmt_args(fmt_args),
|
||||||
|
m_outit(outit){}
|
||||||
|
template<class OutIt, class Char>
|
||||||
|
basic_format_context<OutIt,Char>::basic_format_context(basic_format_args<basic_format_context> fmt_args, iterator outit, const std::locale& l):
|
||||||
|
m_fmt_args(fmt_args),
|
||||||
|
m_outit(outit),
|
||||||
|
m_locale(l){}
|
||||||
|
template<class OutIt, class Char>
|
||||||
|
auto basic_format_context<OutIt,Char>::arg(std::size_t id)const -> basic_format_arg<basic_format_context>{
|
||||||
|
return m_fmt_args.get(id);
|
||||||
|
}
|
||||||
|
template<class OutIt, class Char>
|
||||||
|
auto basic_format_context<OutIt,Char>::arg(const char_type* first, const char_type* last)const -> basic_format_arg<basic_format_context>{
|
||||||
|
return m_fmt_args.get(first, last);
|
||||||
|
}
|
||||||
|
template<class OutIt, class Char>
|
||||||
|
std::size_t basic_format_context<OutIt,Char>::arg_index(const char_type* first, const char_type* last)const{
|
||||||
|
return m_fmt_args.get_index(first, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class OutIt, class Char>
|
||||||
|
std::locale basic_format_context<OutIt,Char>::locale(void){
|
||||||
|
return m_locale;
|
||||||
|
}
|
||||||
|
template<class OutIt, class Char>
|
||||||
|
auto basic_format_context<OutIt,Char>::out(void) -> iterator{
|
||||||
|
return std::move(m_outit);
|
||||||
|
}
|
||||||
|
template<class OutIt, class Char>
|
||||||
|
void basic_format_context<OutIt,Char>::advance_to(iterator it){
|
||||||
|
m_outit = std::move(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
47
include/rexy/detail/format/format_error.hpp
Normal file
47
include/rexy/detail/format/format_error.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_FORMAT_ERROR_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_FORMAT_ERROR_HPP
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
|
#include "../../detail/debug_config.hpp"
|
||||||
|
|
||||||
|
#define REXY_FORMAT_REAL_STRINGIFY(str) #str
|
||||||
|
#define REXY_FORMAT_STRINGIFY(str) REXY_FORMAT_REAL_STRINGIFY(str)
|
||||||
|
#if LIBREXY_ENABLE_DEBUG_LEVEL > 0
|
||||||
|
#define REXY_THROW_FORMAT_ERROR(errstring) throw format_error{__FILE__ ":" REXY_FORMAT_STRINGIFY(__LINE__) ": " errstring}
|
||||||
|
#else
|
||||||
|
#define REXY_THROW_FORMAT_ERROR(errstring) throw format_error{errstring}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace rexy::fmt{
|
||||||
|
|
||||||
|
struct format_error{
|
||||||
|
string_view errstr;
|
||||||
|
|
||||||
|
constexpr const char* what(void)const{
|
||||||
|
return errstr;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
40
include/rexy/detail/format/format_string.hpp
Normal file
40
include/rexy/detail/format/format_string.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_FORMAT_STRING_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_FORMAT_STRING_HPP
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
//Class accepted as an argument to 'format' calls. Will call a constant-evaluated parse of the format string
|
||||||
|
//to check for correctness.
|
||||||
|
template<class Char, class... Args>
|
||||||
|
class basic_format_string
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
basic_string_view<Char> str;
|
||||||
|
|
||||||
|
consteval basic_format_string(const Char* f);
|
||||||
|
consteval basic_format_string(basic_string_view<Char> f);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
43
include/rexy/detail/format/format_string.tpp
Normal file
43
include/rexy/detail/format/format_string.tpp
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_FORMAT_STRING_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_FORMAT_STRING_TPP
|
||||||
|
|
||||||
|
#include "format_string.hpp"
|
||||||
|
|
||||||
|
#include "context_handler.hpp"
|
||||||
|
#include "parse.hpp"
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
template<class Char, class... Args>
|
||||||
|
consteval basic_format_string<Char,Args...>::basic_format_string(const Char* f):
|
||||||
|
basic_format_string(basic_string_view<Char>{f}){}
|
||||||
|
template<class Char, class... Args>
|
||||||
|
consteval basic_format_string<Char,Args...>::basic_format_string(basic_string_view<Char> f):
|
||||||
|
str(f)
|
||||||
|
{
|
||||||
|
parse::perform_parse(format_checker<Char, Args...>{f, sizeof...(Args)}, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
173
include/rexy/detail/format/formatter.hpp
Normal file
173
include/rexy/detail/format/formatter.hpp
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_FORMATTER_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_FORMATTER_HPP
|
||||||
|
|
||||||
|
#include <cstddef> //size_t, nullptr_t
|
||||||
|
#include <variant> //monostate
|
||||||
|
#include <locale> //locale
|
||||||
|
#include <type_traits> //remove_cvref
|
||||||
|
#include <utility> //forward
|
||||||
|
|
||||||
|
#include "basic_types.hpp" //format_specs
|
||||||
|
#include "internal_types.hpp"
|
||||||
|
#include "standard_types.hpp" //basic_parse_context
|
||||||
|
#include "specs_handler.hpp" //format_specs_checker
|
||||||
|
#include "traits.hpp"
|
||||||
|
|
||||||
|
#include "../../string.hpp"
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
#include "../../allocator.hpp"
|
||||||
|
|
||||||
|
namespace rexy::fmt{
|
||||||
|
|
||||||
|
namespace detail{
|
||||||
|
|
||||||
|
namespace format{
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(const void* t, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(const void* t, OutIt out);
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
requires(!UTF8_String<Char>)
|
||||||
|
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||||
|
template<UTF8_String Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(const Char* c, OutIt out);
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(Char b, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(Char b, OutIt out);
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(bool b, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(bool b, OutIt out);
|
||||||
|
template<class Char, class OutIt, Integral T>
|
||||||
|
constexpr OutIt perform_standard_format(T b, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||||
|
template<class Char, class OutIt, Arithmetic T>
|
||||||
|
constexpr OutIt perform_standard_format(T b, OutIt out);
|
||||||
|
template<class Char, class OutIt, Floating T>
|
||||||
|
constexpr OutIt perform_standard_format(T f, OutIt out, const format_specs& specs, const std::locale& loc);
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(std::monostate, OutIt out, const format_specs&, const std::locale&);
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(std::monostate, OutIt out);
|
||||||
|
|
||||||
|
template<class FmtCtx, class ParseCtx, class Specs>
|
||||||
|
struct arg_formatter{
|
||||||
|
FmtCtx& fmt_ctx;
|
||||||
|
ParseCtx& parse_ctx;
|
||||||
|
const Specs& specs;
|
||||||
|
|
||||||
|
template<Handle<FmtCtx> T>
|
||||||
|
constexpr void operator()(T&& t);
|
||||||
|
template<class T>
|
||||||
|
constexpr void operator()(T&& t);
|
||||||
|
};
|
||||||
|
template<class FmtCtx, class ParseCtx>
|
||||||
|
struct empty_formatter{
|
||||||
|
FmtCtx& fmt_ctx;
|
||||||
|
ParseCtx& parse_ctx;
|
||||||
|
|
||||||
|
template<Handle<FmtCtx> T>
|
||||||
|
constexpr void operator()(T&& t);
|
||||||
|
template<class T>
|
||||||
|
constexpr void operator()(T&& t);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class T, class Char>
|
||||||
|
class formatter_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using char_type = Char;
|
||||||
|
using parse_ctx_t = basic_format_parse_context<char_type>;
|
||||||
|
using fmt_ctx_t = detail::fmt_context_t<char_type>;
|
||||||
|
using format_spec_t = detail::dynamic_format_specs<char_type>;
|
||||||
|
using format_spec_handler_t = detail::format_specs_checker<detail::dynamic_format_specs_handler<parse_ctx_t>>;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
format_spec_t specs;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin());
|
||||||
|
template<class U, class FormatContext>
|
||||||
|
auto format(U&& t, FormatContext& ctx) -> decltype(ctx.out());
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace rexy{
|
||||||
|
template<class T, class Char = char>
|
||||||
|
class formatter;
|
||||||
|
|
||||||
|
#define IMPLEMENT_STANDARD_FORMATTER(type) \
|
||||||
|
template<fmt::detail::Formatter_Char C> \
|
||||||
|
class formatter<type, C> : public fmt::detail::formatter_base<fmt::detail::stored_type_t<type,fmt::detail::fmt_context_t<C>>, C>{}
|
||||||
|
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(int);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(unsigned int);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(long long);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(unsigned long long);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(bool);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(float);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(double);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(long double);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(std::nullptr_t);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(short);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(unsigned short);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(long);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(unsigned long);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(char);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(unsigned char);
|
||||||
|
IMPLEMENT_STANDARD_FORMATTER(signed char);
|
||||||
|
|
||||||
|
template<fmt::detail::Formatter_Char C>
|
||||||
|
class formatter<C*,C> : public fmt::detail::formatter_base<const C*,C>{};
|
||||||
|
|
||||||
|
template<fmt::detail::Formatter_Char C>
|
||||||
|
class formatter<const C*,C> : public fmt::detail::formatter_base<const C*,C>{};
|
||||||
|
|
||||||
|
template<fmt::detail::Formatter_Char C, std::size_t I>
|
||||||
|
class formatter<const C[I],C> : public fmt::detail::formatter_base<const C*,C>{};
|
||||||
|
|
||||||
|
template<fmt::detail::Formatter_Char C, REXY_ALLOCATOR_CONCEPT A>
|
||||||
|
class formatter<basic_string<C,A>,C> : public fmt::detail::formatter_base<basic_string_view<C>,C>{};
|
||||||
|
|
||||||
|
template<fmt::detail::Formatter_Char C>
|
||||||
|
class formatter<basic_string_view<C>,C> : public fmt::detail::formatter_base<basic_string_view<C>,C>{};
|
||||||
|
|
||||||
|
template<class T, fmt::detail::Formatter_Char C>
|
||||||
|
class formatter<T*,C> : public fmt::detail::formatter_base<const void*,C>{};
|
||||||
|
|
||||||
|
template<class T, fmt::detail::Formatter_Char C>
|
||||||
|
class formatter<const T*,C> : public fmt::detail::formatter_base<const void*,C>{};
|
||||||
|
|
||||||
|
|
||||||
|
#undef IMPLEMENT_STANDARD_FORMATTER
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
647
include/rexy/detail/format/formatter.tpp
Normal file
647
include/rexy/detail/format/formatter.tpp
Normal file
@ -0,0 +1,647 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_FORMATTER_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_FORMATTER_TPP
|
||||||
|
|
||||||
|
#include "formatter.hpp"
|
||||||
|
|
||||||
|
#include "basic_types.hpp"
|
||||||
|
#include "internal_types.hpp"
|
||||||
|
#include "standard_types.hpp"
|
||||||
|
#include "parse_context.hpp"
|
||||||
|
#include "format_context.hpp"
|
||||||
|
#include "format_args.hpp"
|
||||||
|
#include "parse.hpp"
|
||||||
|
#include "utf_iterator.hpp"
|
||||||
|
|
||||||
|
#include "../../utility.hpp" //abs
|
||||||
|
|
||||||
|
#include <type_traits> //remove_cvref
|
||||||
|
#include <utility> //forward, move
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include <algorithm> //max
|
||||||
|
#include <cmath> //signbit
|
||||||
|
#include <charconv> //to_chars
|
||||||
|
#include <cctype> //toupper
|
||||||
|
#include <cstdint> //uintptr_t
|
||||||
|
#include <locale> //locale
|
||||||
|
#include <variant> //monostate
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
template<class T, class Char>
|
||||||
|
constexpr auto formatter_base<T,Char>::parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()){
|
||||||
|
using stored_type = detail::stored_type_t<std::remove_cvref_t<T>,fmt_ctx_t>;
|
||||||
|
return detail::parse::perform_standard_parse(
|
||||||
|
ctx.begin(),
|
||||||
|
ctx.end(),
|
||||||
|
format_spec_handler_t{
|
||||||
|
detail::dynamic_format_specs_handler<parse_ctx_t>{ctx, specs},
|
||||||
|
detail::map_to_storage_enum_v<stored_type,char_type>
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
template<class T, class Char>
|
||||||
|
template<class U, class FormatContext>
|
||||||
|
auto formatter_base<T,Char>::format(U&& t, FormatContext& ctx) -> decltype(ctx.out()){
|
||||||
|
normalize_dynamic_format_specs(ctx, specs);
|
||||||
|
return detail::format::perform_standard_format<Char>(std::forward<U>(t), ctx.out(), specs);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace format{
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_format_write(const Char* c, OutIt out, std::size_t length){
|
||||||
|
for(std::size_t i = 0;i < length;++i){
|
||||||
|
*out++ = c[i];
|
||||||
|
}
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
template<class OutIt, class Outputer>
|
||||||
|
constexpr OutIt perform_format_write_aligned(OutIt out, int width, const format_specs& specs, alignment default_align, Outputer&& outputer){
|
||||||
|
const int fill_amount = std::max(specs.width - width, 0);
|
||||||
|
int left_fill_amount = fill_amount;
|
||||||
|
int right_fill_amount = fill_amount;
|
||||||
|
const alignment align = specs.align == alignment::none ? default_align : specs.align;
|
||||||
|
|
||||||
|
switch(align){
|
||||||
|
case alignment::center:
|
||||||
|
left_fill_amount /= 2;
|
||||||
|
[[fallthrough]];
|
||||||
|
case alignment::right:
|
||||||
|
for(std::size_t i = 0;i < left_fill_amount;++i){
|
||||||
|
*out++ = specs.align_char;
|
||||||
|
}
|
||||||
|
right_fill_amount -= left_fill_amount;
|
||||||
|
[[fallthrough]];
|
||||||
|
case alignment::left:
|
||||||
|
out = outputer(std::move(out));
|
||||||
|
|
||||||
|
for(std::size_t i = 0;i < right_fill_amount;++i){
|
||||||
|
*out++ = specs.align_char;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid alignment state");
|
||||||
|
};
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
consteval T cxlog10(T t){
|
||||||
|
return t < 10 ? 1 : 1 + cxlog10(t / 10);
|
||||||
|
}
|
||||||
|
template<class OutIt, class T>
|
||||||
|
constexpr OutIt perform_format_write_sign(OutIt out, int sign, T val){
|
||||||
|
if(val < 0){
|
||||||
|
*out++ = '-';
|
||||||
|
}else if(sign == '+'){
|
||||||
|
*out++ = '+';
|
||||||
|
}else if(sign == ' '){
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
template<class OutIt, Floating T>
|
||||||
|
constexpr OutIt perform_format_write_sign(OutIt out, int sign, T val){
|
||||||
|
if(std::signbit(val)){
|
||||||
|
*out++ = '-';
|
||||||
|
}else if(sign == '+'){
|
||||||
|
*out++ = '+';
|
||||||
|
}else if(sign == ' '){
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(const void* t, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||||
|
//2 hexidecimal digits per octet
|
||||||
|
constexpr std::size_t maxbufflen = sizeof(std::uintptr_t) * (CHAR_BIT / 4.0) + 3; //+2 for '0x', +1 for float rounding truncation
|
||||||
|
|
||||||
|
char buff[maxbufflen] = {};
|
||||||
|
buff[0] = '0';
|
||||||
|
buff[1] = 'x';
|
||||||
|
char* buffstart = buff + 2;
|
||||||
|
char* buffend = buff + maxbufflen;
|
||||||
|
|
||||||
|
auto result = std::to_chars(buffstart, buffend, reinterpret_cast<std::uintptr_t>(t), 16);
|
||||||
|
if(result.ec != std::errc{}){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Unable to convert pointer type");
|
||||||
|
}
|
||||||
|
return perform_standard_format<Char>(buff, out, specs, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(const void* t, OutIt out){
|
||||||
|
//2 hexidecimal digits per octet
|
||||||
|
constexpr std::size_t maxbufflen = sizeof(std::uintptr_t) * (CHAR_BIT / 4.0) + 4; //+2 for '0x', +1 for float rounding truncation
|
||||||
|
|
||||||
|
char buff[maxbufflen] = {};
|
||||||
|
buff[0] = '0';
|
||||||
|
buff[1] = 'x';
|
||||||
|
char* buffstart = buff + 2;
|
||||||
|
char* buffend = buff + maxbufflen;
|
||||||
|
|
||||||
|
auto result = std::to_chars(buffstart, buffend, reinterpret_cast<std::uintptr_t>(t), 16);
|
||||||
|
if(result.ec != std::errc{}){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Unable to convert pointer type");
|
||||||
|
}
|
||||||
|
const auto bufflen = result.ptr - buff;
|
||||||
|
return perform_format_write(buff, out, bufflen);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static constexpr unsigned int codepoint_fields[] = {
|
||||||
|
0x1100u, 0x115Fu,
|
||||||
|
0x2329u, 0x232Au,
|
||||||
|
0x2E80u, 0x303Eu,
|
||||||
|
0x3040u, 0xA4CFu,
|
||||||
|
0xAC00u, 0xD7A3u,
|
||||||
|
0xF900u, 0xFAFFu,
|
||||||
|
0xFE10u, 0xFE19u,
|
||||||
|
0xFE30u, 0xFE6Fu,
|
||||||
|
0xFF00u, 0xFF60u,
|
||||||
|
0xFFE0u, 0xFFE6u,
|
||||||
|
0x1F300u, 0x1F64Fu,
|
||||||
|
0x1F900u, 0x1F9FFu,
|
||||||
|
0x20000u, 0x2FFFDu,
|
||||||
|
0x30000u, 0x3FFFDu
|
||||||
|
};
|
||||||
|
constexpr unsigned int estimate_unicode_width(const char32_t c){
|
||||||
|
unsigned int width = 1;
|
||||||
|
for(auto field : codepoint_fields){
|
||||||
|
if(c < field){
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
//flip between 1 and 2
|
||||||
|
width ^= 3u;
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
constexpr unsigned int estimate_unicode_string_width(const char* first, const char* last){
|
||||||
|
unsigned int width = 0;
|
||||||
|
for(utf8_iterator<char> it{first, last};it.valid();++it){
|
||||||
|
const auto codepoint = *it;
|
||||||
|
width += estimate_unicode_width(codepoint);
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
requires(!UTF8_String<Char>)
|
||||||
|
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||||
|
const basic_string_view<Char> str{c};
|
||||||
|
std::size_t est_width = 0;
|
||||||
|
std::size_t print_cnt = 0;
|
||||||
|
if(specs.precision > 0){
|
||||||
|
est_width = std::min<std::size_t>(str.length(), specs.precision);
|
||||||
|
print_cnt = est_width;
|
||||||
|
}else{
|
||||||
|
est_width = str.length();
|
||||||
|
print_cnt = str.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto outputter = [&](OutIt o){
|
||||||
|
for(std::size_t i = 0;i < print_cnt;++i){
|
||||||
|
*o++ = str[i];
|
||||||
|
}
|
||||||
|
return std::move(o);
|
||||||
|
};
|
||||||
|
|
||||||
|
return perform_format_write_aligned(std::move(out), est_width, specs, alignment::left, outputter);
|
||||||
|
}
|
||||||
|
template<UTF8_String Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(const Char* c, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||||
|
const basic_string_view<Char> str{c};
|
||||||
|
std::size_t est_width = 0;
|
||||||
|
std::size_t print_cnt = 0;
|
||||||
|
if(specs.precision > 0){
|
||||||
|
for(utf8_iterator<Char> it{str.begin(), str.end()};it.valid();++it){
|
||||||
|
const auto ch_width = estimate_unicode_width(*it);
|
||||||
|
if(ch_width + est_width > specs.precision){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
print_cnt += it.byte_count();
|
||||||
|
est_width += ch_width;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
est_width = estimate_unicode_string_width(str.cbegin(), str.cend());
|
||||||
|
print_cnt = str.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto outputter = [&](OutIt o){
|
||||||
|
for(std::size_t i = 0;i < print_cnt;++i){
|
||||||
|
*o++ = str[i];
|
||||||
|
}
|
||||||
|
return std::move(o);
|
||||||
|
};
|
||||||
|
|
||||||
|
return perform_format_write_aligned(std::move(out), est_width, specs, alignment::left, outputter);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(const Char* c, OutIt out){
|
||||||
|
for(const Char* i = c;*i;++i){
|
||||||
|
*out++ = *i;
|
||||||
|
}
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(Char b, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||||
|
if(specs.present == presentation::int_t){
|
||||||
|
return perform_standard_format<Char>(static_cast<int>(b), std::move(out), specs, loc);
|
||||||
|
}
|
||||||
|
const auto outputter = [=](OutIt o){
|
||||||
|
*o++ = b;
|
||||||
|
return std::move(o);
|
||||||
|
};
|
||||||
|
return perform_format_write_aligned(std::move(out), 1, specs, alignment::left, outputter);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(Char b, OutIt out){
|
||||||
|
*out++ = b;
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(bool b, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||||
|
switch(specs.present){
|
||||||
|
case presentation::default_t:
|
||||||
|
case presentation::string_t:
|
||||||
|
break;
|
||||||
|
case presentation::int_t:
|
||||||
|
return perform_standard_format<Char>(static_cast<unsigned char>(b), std::move(out), specs, loc);
|
||||||
|
default:
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type argument for bool");
|
||||||
|
};
|
||||||
|
|
||||||
|
if(specs.locale){
|
||||||
|
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
|
||||||
|
const auto word = b ? facet.truename() : facet.falsename();
|
||||||
|
format_specs copy_specs = specs;
|
||||||
|
copy_specs.locale = false;
|
||||||
|
return perform_standard_format<Char>(
|
||||||
|
word.c_str(),
|
||||||
|
std::move(out),
|
||||||
|
copy_specs,
|
||||||
|
loc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return perform_standard_format<Char>(b ? "true" : "false", std::move(out), specs, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(bool b, OutIt out){
|
||||||
|
return perform_standard_format<Char>(b ? "true" : "false", std::move(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_localized_integer_write(OutIt out, const char* start, const char* last, const std::locale& loc){
|
||||||
|
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
|
||||||
|
const int group_size = facet.grouping()[0];
|
||||||
|
const Char sep = facet.thousands_sep();
|
||||||
|
const int len = last - start;
|
||||||
|
if(group_size != 0){
|
||||||
|
int write_count = (len-1) % group_size;
|
||||||
|
*out++ = *start++;
|
||||||
|
while(true){
|
||||||
|
for(;write_count > 0;--write_count){
|
||||||
|
*out++ = *start++;
|
||||||
|
}
|
||||||
|
if(start == last){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*out++ = sep;
|
||||||
|
write_count = std::min(group_size, int{last - start});
|
||||||
|
}
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
return perform_format_write(start, std::move(out), len);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class OutIt, Integral T>
|
||||||
|
constexpr OutIt perform_standard_format(T b, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||||
|
constexpr std::size_t maxbufflen = rexy::max(
|
||||||
|
std::numeric_limits<long long>::digits + 3, //add 1 for sign bit, 2 for prefix
|
||||||
|
std::numeric_limits<unsigned long long>::digits + 3
|
||||||
|
);
|
||||||
|
if(specs.present == presentation::char_t){
|
||||||
|
return perform_standard_format<Char>(static_cast<Char>(b), std::move(out), specs, loc);
|
||||||
|
};
|
||||||
|
|
||||||
|
int base = 10;
|
||||||
|
int total_width = 0;
|
||||||
|
const bool should_zero_fill = specs.zero_fill && specs.align == alignment::none;
|
||||||
|
bool to_upper = false;
|
||||||
|
char buff[maxbufflen] = {};
|
||||||
|
char* buffstart = buff;
|
||||||
|
char* buffend = buff + maxbufflen;
|
||||||
|
string_view prefix = "";
|
||||||
|
|
||||||
|
switch(specs.type){
|
||||||
|
case 'b':
|
||||||
|
prefix = "0b";
|
||||||
|
base = 2;
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
prefix = "0B";
|
||||||
|
base = 2;
|
||||||
|
break;
|
||||||
|
case 'o':
|
||||||
|
prefix = "0";
|
||||||
|
base = 8;
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
prefix = "0x";
|
||||||
|
base = 16;
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
prefix = "0X";
|
||||||
|
to_upper = true;
|
||||||
|
base = 16;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
total_width += prefix.length();
|
||||||
|
|
||||||
|
auto result = std::to_chars(buffstart, buffend, b, base);
|
||||||
|
if(result.ec != std::errc{}){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Unable to convert integral type");
|
||||||
|
}
|
||||||
|
buffend = result.ptr;
|
||||||
|
if(b < 0){
|
||||||
|
++buffstart;
|
||||||
|
total_width += 1;
|
||||||
|
}else if(specs.sign != 0){
|
||||||
|
total_width += 1;
|
||||||
|
}
|
||||||
|
const auto bufflen = buffend - buffstart;
|
||||||
|
total_width += bufflen;
|
||||||
|
|
||||||
|
if(to_upper){
|
||||||
|
for(auto it = buffstart;it != buffend;++it){
|
||||||
|
*it = std::toupper(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto outputter = [&](OutIt o) -> OutIt{
|
||||||
|
if(specs.alt_form){
|
||||||
|
o = perform_format_write_sign(std::move(o), specs.sign, b);
|
||||||
|
o = perform_format_write(prefix.data(), std::move(o), prefix.length());
|
||||||
|
}else{
|
||||||
|
o = perform_format_write(prefix.data(), std::move(out), prefix.length());
|
||||||
|
o = perform_format_write_sign(std::move(o), specs.sign, b);
|
||||||
|
}
|
||||||
|
if(should_zero_fill && specs.width > total_width){
|
||||||
|
const int fill_width = specs.width - total_width;
|
||||||
|
for(int i = 0;i < fill_width;++i){
|
||||||
|
*o++ = '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(specs.locale){
|
||||||
|
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
|
||||||
|
const int group_size = facet.grouping()[0];
|
||||||
|
const Char sep = facet.thousands_sep();
|
||||||
|
return perform_localized_integer_write<Char>(std::move(o), buffstart, buffend, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
return perform_format_write(buffstart, std::move(o), bufflen);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(should_zero_fill){
|
||||||
|
return outputter(std::move(out));
|
||||||
|
}
|
||||||
|
return perform_format_write_aligned(std::move(out), total_width, specs, alignment::right, outputter);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////DONE///////////////////////////////////////////////
|
||||||
|
template<class Char, class OutIt, Arithmetic T>
|
||||||
|
constexpr OutIt perform_standard_format(T b, OutIt out){
|
||||||
|
using limits = std::numeric_limits<long double>;
|
||||||
|
constexpr auto maxexp = limits::max_exponent10; //this is a constant expression but using limits::max_exponent10 directly isn't?
|
||||||
|
//long double will be the longest type possible, so operate on that.
|
||||||
|
//+4 for ones' place digit, decimal point, and 'e+' in scientific mode
|
||||||
|
//maximum buffer length is the maximum significant digits plus maximum length of exponent
|
||||||
|
constexpr std::size_t maxbufflen = 4 + limits::max_digits10 + cxlog10(maxexp);
|
||||||
|
|
||||||
|
char buff[maxbufflen] = {};
|
||||||
|
char* buffend = buff + maxbufflen;
|
||||||
|
|
||||||
|
auto result = std::to_chars(buff, buffend, b);
|
||||||
|
if(result.ec != std::errc{}){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Unable to convert arithmetic type");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto bufflen = result.ptr - buff;
|
||||||
|
return perform_format_write(buff, std::move(out), bufflen);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
template<class Char, class OutIt, Floating T>
|
||||||
|
constexpr OutIt perform_standard_format(T f, OutIt out, const format_specs& specs, const std::locale& loc){
|
||||||
|
using limits = std::numeric_limits<T>;
|
||||||
|
//max number of post-decimal digits is same as the inverted smallest radix exponent
|
||||||
|
constexpr int max_precision = rexy::abs(limits::min_exponent);
|
||||||
|
//max number of leading digits is same as biggest decimal exponent
|
||||||
|
constexpr int max_significants = limits::max_exponent10;
|
||||||
|
//+4 for ones' place digit, decimal point, and 'e+' in scientific mode
|
||||||
|
//maximum buffer length is the maximum significant digits plus maximum precision because the
|
||||||
|
//user can request any precision. So you can take the longest number with no decimal and add on
|
||||||
|
//the longest decimal trail allowed.
|
||||||
|
constexpr int maxbufflen = max_precision + max_significants + 4;
|
||||||
|
|
||||||
|
char buff[maxbufflen] = {};
|
||||||
|
char* buffstart = buff;
|
||||||
|
char* buffend = buff + maxbufflen;
|
||||||
|
|
||||||
|
const bool supplied_precision = specs.precision > 0;
|
||||||
|
const bool is_infinity = f == std::numeric_limits<T>::infinity() || f == -std::numeric_limits<T>::infinity();
|
||||||
|
const bool is_nan = (f != f);
|
||||||
|
const bool is_integer_representable = (static_cast<T>(static_cast<long long>(f)) == f) && !is_infinity && !is_nan;
|
||||||
|
const bool should_zero_fill = specs.zero_fill && specs.align == alignment::none;
|
||||||
|
|
||||||
|
std::chars_format fmt = std::chars_format::general;
|
||||||
|
bool to_upper = false;
|
||||||
|
bool manual_precision = supplied_precision;
|
||||||
|
bool trailing_dot = false;
|
||||||
|
std::size_t total_width = 0;
|
||||||
|
|
||||||
|
//TODO handle any other modes and formatting options
|
||||||
|
std::to_chars_result result{};
|
||||||
|
switch(specs.type){
|
||||||
|
case 'A':
|
||||||
|
to_upper = true;
|
||||||
|
[[fallthrough]];
|
||||||
|
case 'a':
|
||||||
|
fmt = std::chars_format::hex;
|
||||||
|
break;
|
||||||
|
case 'E':
|
||||||
|
to_upper = true;
|
||||||
|
[[fallthrough]];
|
||||||
|
case 'e':
|
||||||
|
fmt = std::chars_format::scientific;
|
||||||
|
manual_precision = true;
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
to_upper = true;
|
||||||
|
[[fallthrough]];
|
||||||
|
case 'f':
|
||||||
|
fmt = std::chars_format::fixed;
|
||||||
|
manual_precision = true;
|
||||||
|
break;
|
||||||
|
case 'G':
|
||||||
|
to_upper = true;
|
||||||
|
[[fallthrough]];
|
||||||
|
case 'g':
|
||||||
|
manual_precision = true;
|
||||||
|
if(specs.alt_form){
|
||||||
|
//keep trailing zeros
|
||||||
|
fmt = std::chars_format::fixed;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
trailing_dot = is_integer_representable && !supplied_precision && specs.alt_form;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
if(manual_precision){
|
||||||
|
const int precision = supplied_precision ? specs.precision : 6;
|
||||||
|
result = std::to_chars(buffstart, buffend, f, fmt, precision);
|
||||||
|
}else{
|
||||||
|
result = std::to_chars(buffstart, buffend, f, fmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result.ec != std::errc{}){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Unable to convert floating type");
|
||||||
|
}
|
||||||
|
buffend = result.ptr;
|
||||||
|
|
||||||
|
//exclude negative sign automatically put there
|
||||||
|
if(buffstart[0] == '-'){
|
||||||
|
++buffstart;
|
||||||
|
++total_width;
|
||||||
|
}else if(specs.sign != 0){
|
||||||
|
++total_width;
|
||||||
|
}
|
||||||
|
if(trailing_dot){
|
||||||
|
++total_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto bufflen = buffend - buffstart;
|
||||||
|
total_width += bufflen;
|
||||||
|
|
||||||
|
if(to_upper){
|
||||||
|
for(auto it = buffstart;it != buffend;++it){
|
||||||
|
*it = std::toupper(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto outputter = [&](OutIt o){
|
||||||
|
Char radix_char = '.';
|
||||||
|
o = perform_format_write_sign(std::move(o), specs.sign, f);
|
||||||
|
if(should_zero_fill && specs.width > total_width){
|
||||||
|
const int fill_width = specs.width - total_width;
|
||||||
|
for(int i = 0;i < fill_width;++i){
|
||||||
|
*o++ = '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(specs.locale){
|
||||||
|
const auto& facet = std::use_facet<std::numpunct<Char>>(loc);
|
||||||
|
const int group_size = facet.grouping()[0];
|
||||||
|
radix_char = facet.decimal_point();
|
||||||
|
string_view buff_view{buffstart, buffend};
|
||||||
|
const auto radix_pos = buff_view.find_first_of('.');
|
||||||
|
if(radix_pos != string_view::npos){
|
||||||
|
buff[radix_pos] = radix_char;
|
||||||
|
o = perform_localized_integer_write<Char>(std::move(o), buffstart, buffstart + radix_pos, loc);
|
||||||
|
buffstart += radix_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
o = perform_format_write(buffstart, std::move(o), bufflen);
|
||||||
|
if(trailing_dot){
|
||||||
|
*o++ = radix_char;
|
||||||
|
}
|
||||||
|
return std::move(o);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(should_zero_fill){
|
||||||
|
return outputter(std::move(out));
|
||||||
|
}
|
||||||
|
return perform_format_write_aligned(std::move(out), total_width, specs, alignment::right, outputter);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(std::monostate, OutIt out, const format_specs&, const std::locale&){
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt perform_standard_format(std::monostate, OutIt out){
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class FmtCtx, class ParseCtx, class Specs>
|
||||||
|
template<Handle<FmtCtx> T>
|
||||||
|
constexpr void arg_formatter<FmtCtx,ParseCtx,Specs>::operator()(T&& t){
|
||||||
|
t.format(parse_ctx, fmt_ctx);
|
||||||
|
}
|
||||||
|
template<class FmtCtx, class ParseCtx, class Specs>
|
||||||
|
template<class T>
|
||||||
|
constexpr void arg_formatter<FmtCtx,ParseCtx,Specs>::operator()(T&& t){
|
||||||
|
using handler_t = format_specs_checker<dynamic_format_specs_handler<ParseCtx>>;
|
||||||
|
using specs_t = dynamic_format_specs<typename FmtCtx::char_type>;
|
||||||
|
|
||||||
|
specs_t specs;
|
||||||
|
parse_ctx.advance_to(parse::perform_standard_parse(
|
||||||
|
parse_ctx.begin(),
|
||||||
|
parse_ctx.end(),
|
||||||
|
handler_t{
|
||||||
|
dynamic_format_specs_handler<ParseCtx>{
|
||||||
|
parse_ctx,
|
||||||
|
specs
|
||||||
|
},
|
||||||
|
map_to_storage_enum_v<T,typename FmtCtx::char_type>
|
||||||
|
}
|
||||||
|
));
|
||||||
|
normalize_dynamic_format_specs(fmt_ctx, specs);
|
||||||
|
fmt_ctx.advance_to(perform_standard_format<typename FmtCtx::char_type>(t, fmt_ctx.out(), specs, fmt_ctx.locale()));
|
||||||
|
}
|
||||||
|
template<class FmtCtx, class ParseCtx>
|
||||||
|
template<Handle<FmtCtx> T>
|
||||||
|
constexpr void empty_formatter<FmtCtx,ParseCtx>::operator()(T&& t){
|
||||||
|
t.format(parse_ctx, fmt_ctx);
|
||||||
|
}
|
||||||
|
template<class FmtCtx, class ParseCtx>
|
||||||
|
template<class T>
|
||||||
|
constexpr void empty_formatter<FmtCtx,ParseCtx>::operator()(T&& t){
|
||||||
|
perform_standard_format<typename FmtCtx::char_type>(t, fmt_ctx.out());
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace format
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
83
include/rexy/detail/format/internal_types.hpp
Normal file
83
include/rexy/detail/format/internal_types.hpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_INTERNAL_TYPES_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_INTERNAL_TYPES_HPP
|
||||||
|
|
||||||
|
#include "../../cx/string.hpp"
|
||||||
|
#include "standard_types.hpp"
|
||||||
|
|
||||||
|
#include <iterator> //back_insert_iterator
|
||||||
|
#include <type_traits> //type_identity
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
//forward declare implementation types
|
||||||
|
template<class Char, class... Args>
|
||||||
|
class basic_format_string;
|
||||||
|
template<class Context, class... Args>
|
||||||
|
class basic_format_arg_store;
|
||||||
|
template<class Char>
|
||||||
|
class format_output_buffer_base;
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
class basic_format_output_buffer;
|
||||||
|
struct format_specs;
|
||||||
|
template<class Char>
|
||||||
|
struct dynamic_format_specs;
|
||||||
|
template<class ParseCtx, class... Args>
|
||||||
|
struct checker_format_specs_handler;
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
struct format_specs_handler;
|
||||||
|
template<class ParseCtx>
|
||||||
|
struct dynamic_format_specs_handler;
|
||||||
|
template<class T, class Char>
|
||||||
|
struct runtime_arg;
|
||||||
|
template<class T, rexy::cx::string Name>
|
||||||
|
struct static_arg;
|
||||||
|
|
||||||
|
//aliases for easier access to both implementation and standard defined types
|
||||||
|
template<class Char>
|
||||||
|
using fmt_buffer_t = format_output_buffer_base<Char>;
|
||||||
|
template<class Char>
|
||||||
|
using output_iterator_t = std::back_insert_iterator<fmt_buffer_t<Char>>;
|
||||||
|
template<class Char>
|
||||||
|
using fmt_context_t = basic_format_context<output_iterator_t<Char>,Char>;
|
||||||
|
template<class Char>
|
||||||
|
using parse_context_t = basic_format_parse_context<Char>;
|
||||||
|
template<class... Args>
|
||||||
|
using format_arg_store = basic_format_arg_store<fmt_context_t<char>, Args...>;
|
||||||
|
template<class... Args>
|
||||||
|
using wformat_arg_store = basic_format_arg_store<fmt_context_t<wchar_t>, Args...>;
|
||||||
|
template<class... Args>
|
||||||
|
using format_string = basic_format_string<char,std::type_identity_t<Args>...>;
|
||||||
|
template<class... Args>
|
||||||
|
using wformat_string = basic_format_string<wchar_t, std::type_identity_t<Args>...>;
|
||||||
|
template<class OutIt>
|
||||||
|
using format_output_buffer = basic_format_output_buffer<char,OutIt>;
|
||||||
|
template<class OutIt>
|
||||||
|
using wformat_output_buffer = basic_format_output_buffer<wchar_t,OutIt>;
|
||||||
|
|
||||||
|
template<class Char>
|
||||||
|
using impl_format_args = basic_format_args<fmt_context_t<Char>>;
|
||||||
|
template<class Char>
|
||||||
|
using format_arg = basic_format_arg<fmt_context_t<Char>>;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
117
include/rexy/detail/format/named_args.hpp
Normal file
117
include/rexy/detail/format/named_args.hpp
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_NAMED_ARGS_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_NAMED_ARGS_HPP
|
||||||
|
|
||||||
|
#include <type_traits> //remove_cvref, declval, integral_constant
|
||||||
|
#include <utility> //forward
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
|
namespace rexy::fmt{
|
||||||
|
|
||||||
|
namespace detail{
|
||||||
|
|
||||||
|
template<class T, class Char>
|
||||||
|
struct arg_base{
|
||||||
|
const T& value;
|
||||||
|
const basic_string_view<Char> name;
|
||||||
|
};
|
||||||
|
|
||||||
|
//class used to store name and value for runtime analysis of named arguments
|
||||||
|
//the type T is the actual formatted type and name is used only for lookup
|
||||||
|
//during runtime formatting
|
||||||
|
template<class T, class Char>
|
||||||
|
struct runtime_arg : public arg_base<T,Char>{
|
||||||
|
using char_type = Char;
|
||||||
|
using value_type = std::remove_cvref_t<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
//class used to store name and value for compile time analysis of named arguments
|
||||||
|
//the type T is the actual formatted type and Name is used for name lookup in
|
||||||
|
//format string checking
|
||||||
|
template<class T, rexy::cx::string Name>
|
||||||
|
struct static_arg : public arg_base<T,typename decltype(Name)::value_type>{
|
||||||
|
using char_type = typename decltype(Name)::value_type;
|
||||||
|
using value_type = std::remove_cvref_t<T>;
|
||||||
|
static constexpr auto static_name = Name;
|
||||||
|
|
||||||
|
constexpr static_arg(const T& v):
|
||||||
|
arg_base<T,char_type>{v, {Name.c_str(), Name.length()}}{}
|
||||||
|
static_arg(const static_arg&) = delete;
|
||||||
|
constexpr static_arg(static_arg&&) = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
//temporary object to store Name to pass off to a compile time argument during assignment
|
||||||
|
template<rexy::cx::string Name>
|
||||||
|
struct arg_literal_result{
|
||||||
|
template<class T>
|
||||||
|
constexpr auto operator=(T&& t){
|
||||||
|
return static_arg<std::remove_cvref_t<T>, Name>{std::forward<T>(t)};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct is_runtime_named_arg{
|
||||||
|
template<class U, class Char>
|
||||||
|
static auto check(runtime_arg<U,Char>) -> std::true_type;
|
||||||
|
static auto check(...) -> std::false_type;
|
||||||
|
|
||||||
|
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>>()))::value;
|
||||||
|
};
|
||||||
|
template<class T>
|
||||||
|
static constexpr bool is_runtime_named_arg_v = is_runtime_named_arg<T>::value;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct is_static_named_arg{
|
||||||
|
template<class U, rexy::cx::string Name>
|
||||||
|
static auto check(static_arg<U,Name>) -> std::true_type;
|
||||||
|
static auto check(...) -> std::false_type;
|
||||||
|
|
||||||
|
static constexpr bool value = decltype(check(std::declval<std::remove_cvref_t<T>>()))::value;
|
||||||
|
};
|
||||||
|
template<class T>
|
||||||
|
static constexpr bool is_static_named_arg_v = is_static_named_arg<T>::value;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
concept RuntimeNamedArg = is_runtime_named_arg_v<T>;
|
||||||
|
template<class T>
|
||||||
|
concept StaticNamedArg = is_static_named_arg_v<T>;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
concept NamedArg = StaticNamedArg<T> || RuntimeNamedArg<T>;
|
||||||
|
|
||||||
|
template<class It, std::size_t I, class Arg, class... Args>
|
||||||
|
constexpr std::size_t find_static_named_arg_id_impl(It first, It last);
|
||||||
|
template<class It, class... Args>
|
||||||
|
constexpr std::size_t find_static_named_arg_id(It first, It last);
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
struct count_named_args{
|
||||||
|
static constexpr std::size_t value = (std::size_t{NamedArg<Args>} + ...);
|
||||||
|
};
|
||||||
|
template<class... Args>
|
||||||
|
static constexpr std::size_t count_named_args_v = count_named_args<Args...>::value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
57
include/rexy/detail/format/named_args.tpp
Normal file
57
include/rexy/detail/format/named_args.tpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_NAMED_ARGS_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_NAMED_ARGS_TPP
|
||||||
|
|
||||||
|
#include "named_args.hpp"
|
||||||
|
|
||||||
|
#include "basic_types.hpp"
|
||||||
|
#include "../../utility.hpp" //strcmp
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
template<class It, std::size_t I, class Arg, class... Args>
|
||||||
|
constexpr std::size_t find_static_named_arg_id_impl(It first, It last){
|
||||||
|
if constexpr(StaticNamedArg<Arg>){
|
||||||
|
const auto len = last - first;
|
||||||
|
const auto arg_len = Arg::static_name.length();
|
||||||
|
if(len == arg_len){
|
||||||
|
if(!rexy::strncmp(first, Arg::static_name.c_str(), arg_len)){
|
||||||
|
return I;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if constexpr(sizeof...(Args)){
|
||||||
|
return find_static_named_arg_id_impl<It,I+1,Args...>(first, last);
|
||||||
|
}else{
|
||||||
|
return invalid_arg_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<class It, class... Args>
|
||||||
|
constexpr std::size_t find_static_named_arg_id(It first, It last){
|
||||||
|
if constexpr(!sizeof...(Args)){
|
||||||
|
return invalid_arg_index;
|
||||||
|
}else{
|
||||||
|
return find_static_named_arg_id_impl<It, 0, Args...>(first, last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
131
include/rexy/detail/format/output_buffer.hpp
Normal file
131
include/rexy/detail/format/output_buffer.hpp
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_OUTPUT_BUFFER_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_OUTPUT_BUFFER_HPP
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include <cstdio> //fputc, FILE
|
||||||
|
#include <cwchar> //fputwc
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
//polymorphic buffer of which a reference can be passed around to prevent
|
||||||
|
//passing around multiple output iterator types
|
||||||
|
template<class Char>
|
||||||
|
class format_output_buffer_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = Char;
|
||||||
|
protected:
|
||||||
|
static constexpr std::size_t s_bufsize = 256;
|
||||||
|
protected:
|
||||||
|
Char m_data[s_bufsize];
|
||||||
|
std::size_t m_size = 0;
|
||||||
|
std::size_t m_written_count = 0;
|
||||||
|
public:
|
||||||
|
format_output_buffer_base(void) = default;
|
||||||
|
virtual ~format_output_buffer_base(void) = default;
|
||||||
|
|
||||||
|
virtual std::size_t write_out(void) = 0;
|
||||||
|
void clear(void);
|
||||||
|
void push_back(Char c);
|
||||||
|
|
||||||
|
constexpr std::size_t count(void)const;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Used with the 'formatter' to output a type
|
||||||
|
//OutIt is an iterator where the output is written at the end.
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
class basic_format_output_buffer : public format_output_buffer_base<Char>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using base = format_output_buffer_base<Char>;
|
||||||
|
private:
|
||||||
|
OutIt m_out;
|
||||||
|
public:
|
||||||
|
basic_format_output_buffer(OutIt out);
|
||||||
|
~basic_format_output_buffer(void)override;
|
||||||
|
|
||||||
|
std::size_t write_out(void)override;
|
||||||
|
|
||||||
|
constexpr OutIt out(void);
|
||||||
|
private:
|
||||||
|
std::size_t write_out_simple(void);
|
||||||
|
};
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
class basic_format_output_n_buffer : public format_output_buffer_base<Char>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using base = format_output_buffer_base<Char>;
|
||||||
|
private:
|
||||||
|
OutIt m_out;
|
||||||
|
std::size_t m_max_write = 0;
|
||||||
|
public:
|
||||||
|
basic_format_output_n_buffer(OutIt out, std::size_t max);
|
||||||
|
~basic_format_output_n_buffer(void)override;
|
||||||
|
|
||||||
|
std::size_t write_out(void)override;
|
||||||
|
|
||||||
|
constexpr OutIt out(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Char>
|
||||||
|
class basic_format_size_buffer : public format_output_buffer_base<Char>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr basic_format_size_buffer(void) = default;
|
||||||
|
constexpr ~basic_format_size_buffer(void)override;
|
||||||
|
|
||||||
|
std::size_t write_out(void)override;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class Char>
|
||||||
|
struct print_iterator;
|
||||||
|
template<>
|
||||||
|
struct print_iterator<char>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
FILE* m_stream;
|
||||||
|
public:
|
||||||
|
constexpr print_iterator(FILE* stream):
|
||||||
|
m_stream(stream){}
|
||||||
|
print_iterator& operator=(char c){fputc(c, m_stream);return *this;}
|
||||||
|
|
||||||
|
constexpr print_iterator& operator*(void){return *this;}
|
||||||
|
constexpr print_iterator& operator++(void){return *this;}
|
||||||
|
constexpr print_iterator operator++(int){return *this;}
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct print_iterator<wchar_t>
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
FILE* m_stream;
|
||||||
|
public:
|
||||||
|
constexpr print_iterator(FILE* stream):
|
||||||
|
m_stream(stream){}
|
||||||
|
print_iterator& operator=(wchar_t c){fputwc(c, m_stream);return *this;}
|
||||||
|
|
||||||
|
constexpr print_iterator& operator*(void){return *this;}
|
||||||
|
constexpr print_iterator& operator++(void){return *this;}
|
||||||
|
constexpr print_iterator operator++(int){return *this;}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
156
include/rexy/detail/format/output_buffer.tpp
Normal file
156
include/rexy/detail/format/output_buffer.tpp
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_OUTPUT_BUFFER_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_OUTPUT_BUFFER_TPP
|
||||||
|
|
||||||
|
#include "output_buffer.hpp"
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include <utility> //move
|
||||||
|
#include <type_traits> //is_pointer, remove_cvref, integral_constant
|
||||||
|
#include <algorithm> //min
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
///////////////////////format_output_buffer_base////////////////////////
|
||||||
|
template<class Char>
|
||||||
|
void format_output_buffer_base<Char>::clear(void){
|
||||||
|
m_size = 0;
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
void format_output_buffer_base<Char>::push_back(Char c){
|
||||||
|
if(m_size == s_bufsize){
|
||||||
|
m_written_count += write_out();
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
m_data[m_size++] = c;
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
constexpr std::size_t format_output_buffer_base<Char>::count(void)const{
|
||||||
|
//include data that will be written during next call to write_out
|
||||||
|
return m_written_count + m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////basic_format_output_buffer////////////////////////
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
basic_format_output_buffer<Char,OutIt>::basic_format_output_buffer(OutIt out):
|
||||||
|
m_out(out){}
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
basic_format_output_buffer<Char,OutIt>::~basic_format_output_buffer(void){
|
||||||
|
write_out();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
concept HasContainerAlias = requires{
|
||||||
|
typename T::container_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, class It>
|
||||||
|
concept HasInsertMethod = requires(T t, It i){
|
||||||
|
{t.insert(0, i, i)};
|
||||||
|
{t.size()};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct container_traits_helper{
|
||||||
|
using container = int;
|
||||||
|
};
|
||||||
|
template<class It>
|
||||||
|
struct container_traits : public It, public container_traits_helper{
|
||||||
|
template<class T = container_traits, class U = typename T::container>
|
||||||
|
static std::false_type check(int);
|
||||||
|
static std::true_type check(...);
|
||||||
|
};
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
OutIt real_write_out(const Char* start, std::size_t write_count, OutIt out){
|
||||||
|
static constexpr bool has_container = decltype(container_traits<OutIt>::check(0))::value;
|
||||||
|
|
||||||
|
//optimize for types where direct access to the underlying container is available
|
||||||
|
//check for a container member and a 'container_type' typedef in OutIt
|
||||||
|
//reason for the type check is that without 'container_type' it might not be safe to access the 'container'
|
||||||
|
if constexpr(has_container && HasContainerAlias<OutIt>){
|
||||||
|
struct container_access : public OutIt{
|
||||||
|
using OutIt::container;
|
||||||
|
};
|
||||||
|
container_access ca{out};
|
||||||
|
auto& container = ca.container;
|
||||||
|
|
||||||
|
if constexpr(HasInsertMethod<typename OutIt::container_type,const Char*>){
|
||||||
|
if constexpr(std::is_pointer_v<std::remove_cvref_t<decltype(container)>>){
|
||||||
|
container->insert(container->size(), start, start + write_count);
|
||||||
|
}else{
|
||||||
|
container.insert(container.size(), start, start + write_count);
|
||||||
|
}
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(std::size_t i = 0;i < write_count;++i){
|
||||||
|
out = start[i];
|
||||||
|
}
|
||||||
|
return std::move(out);
|
||||||
|
}
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
std::size_t basic_format_output_buffer<Char,OutIt>::write_out(void){
|
||||||
|
m_out = real_write_out(this->m_data, this->m_size, std::move(m_out));
|
||||||
|
return this->m_size;
|
||||||
|
}
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt basic_format_output_buffer<Char,OutIt>::out(void){
|
||||||
|
return std::move(m_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////basic_format_output_n_buffer////////////////////////
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
basic_format_output_n_buffer<Char,OutIt>::basic_format_output_n_buffer(OutIt out, std::size_t max):
|
||||||
|
m_out(out),
|
||||||
|
m_max_write(max){}
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
basic_format_output_n_buffer<Char,OutIt>::~basic_format_output_n_buffer(void){
|
||||||
|
write_out();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
std::size_t basic_format_output_n_buffer<Char,OutIt>::write_out(void){
|
||||||
|
const auto to_write = std::min(this->size, m_max_write);
|
||||||
|
|
||||||
|
m_out = real_write_out(this->m_data, to_write, std::move(m_out));
|
||||||
|
m_max_write -= to_write;
|
||||||
|
return to_write;
|
||||||
|
}
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
constexpr OutIt basic_format_output_n_buffer<Char,OutIt>::out(void){
|
||||||
|
return std::move(m_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////basic_format_size_buffer//////////////////////////
|
||||||
|
template<class Char>
|
||||||
|
constexpr basic_format_size_buffer<Char>::~basic_format_size_buffer(void){
|
||||||
|
write_out();
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
std::size_t basic_format_size_buffer<Char>::write_out(void){
|
||||||
|
return this->m_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
116
include/rexy/detail/format/parse.hpp
Normal file
116
include/rexy/detail/format/parse.hpp
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_PARSE_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_PARSE_HPP
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail::parse{
|
||||||
|
|
||||||
|
//A few basic string checking functions
|
||||||
|
template<class Char>
|
||||||
|
constexpr auto find_first_of_it(Char v,
|
||||||
|
typename basic_string_view<Char>::const_iterator start_it,
|
||||||
|
typename basic_string_view<Char>::const_iterator last_it);
|
||||||
|
template<class Char>
|
||||||
|
constexpr auto find_first_of_it(const basic_string_view<Char>& str, Char v);
|
||||||
|
template<class It>
|
||||||
|
constexpr bool check_duplicate_char(It start, It end);
|
||||||
|
template<class Char>
|
||||||
|
constexpr bool is_a_number(Char c);
|
||||||
|
template<class It>
|
||||||
|
constexpr int to_integer(It start, It end);
|
||||||
|
template<class It>
|
||||||
|
constexpr It find_valid_index(It start, It end);
|
||||||
|
|
||||||
|
//Used with 'visit_format_arg' as the 'fun' arg. Gets the value of the argument as a 'long long'
|
||||||
|
//if the argument is of integral type. Throws an error otherwise
|
||||||
|
struct dynamic_integer_retriever{
|
||||||
|
template<class T>
|
||||||
|
constexpr long long operator()(T&& t);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Used during call to 'perform_standard_nested_replacement_field' as the 'Adapter' type.
|
||||||
|
//Calls 'on_dynamic_width' of 'specs'
|
||||||
|
template<class Specs>
|
||||||
|
struct dynamic_width_adapter{
|
||||||
|
Specs& specs;
|
||||||
|
|
||||||
|
constexpr decltype(auto) operator()(void);
|
||||||
|
constexpr decltype(auto) operator()(int i);
|
||||||
|
template<class It>
|
||||||
|
constexpr decltype(auto) operator()(It start, It last);
|
||||||
|
};
|
||||||
|
//Used during call to 'perform_standard_nested_replacement_field' as the 'Adapter' type.
|
||||||
|
//Calls 'on_dynamic_precision' of 'specs'
|
||||||
|
template<class Specs>
|
||||||
|
struct dynamic_precision_adapter{
|
||||||
|
Specs& specs;
|
||||||
|
|
||||||
|
constexpr decltype(auto) operator()(void);
|
||||||
|
constexpr decltype(auto) operator()(int i);
|
||||||
|
template<class It>
|
||||||
|
constexpr decltype(auto) operator()(It start, It last);
|
||||||
|
};
|
||||||
|
template<class Handler>
|
||||||
|
struct dynamic_index_adapter{
|
||||||
|
Handler& handler;
|
||||||
|
std::size_t& id;
|
||||||
|
constexpr decltype(auto) operator()(void);
|
||||||
|
constexpr decltype(auto) operator()(int i);
|
||||||
|
template<class It>
|
||||||
|
constexpr decltype(auto) operator()(It start, It last);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Standalone functions for parsing standard format fields
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_fill_align_option(It start, It last, FormatSpecs&& specs);
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_sign_option(It start, It last, FormatSpecs&& specs);
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_alt_form_option(It start, It last, FormatSpecs&& specs);
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_zero_fill_option(It start, It last, FormatSpecs&& specs);
|
||||||
|
template<class It, class Adapter>
|
||||||
|
constexpr It perform_standard_nested_replacement_field(It start, It last, Adapter&& func);
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_width_option(It start, It last, FormatSpecs&& specs);
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_precision_option(It start, It last, FormatSpecs&& specs);
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_locale_option(It start, It last, FormatSpecs&& specs);
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_type_option(It start, It last, FormatSpecs&& specs);
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_parse(It start, It last, FormatSpecs&& specs);
|
||||||
|
|
||||||
|
//Standalone functions for parsing the entire format string
|
||||||
|
template<class Handler, class It>
|
||||||
|
constexpr It perform_format_index_spec(Handler&& handler, It start, It last, std::size_t& id);
|
||||||
|
template<class Handler, class It>
|
||||||
|
constexpr It perform_empty_format_field_parse(Handler&& handler, It start, It last);
|
||||||
|
template<class Handler, class It>
|
||||||
|
constexpr It perform_format_field_parse(Handler&& handler, It start, It last);
|
||||||
|
template<class Handler, class Char>
|
||||||
|
constexpr void perform_parse(Handler&& handler, basic_string_view<Char> fmt);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
355
include/rexy/detail/format/parse.tpp
Normal file
355
include/rexy/detail/format/parse.tpp
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_PARSE_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_PARSE_TPP
|
||||||
|
|
||||||
|
#include "parse.hpp"
|
||||||
|
|
||||||
|
#include "format_error.hpp"
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include <type_traits> //is_integral, remove_cvref, add_lvalue_reference, remove_reference
|
||||||
|
#include <algorithm> //min
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail::parse{
|
||||||
|
|
||||||
|
template<class Char>
|
||||||
|
constexpr auto find_first_of_it(Char v,
|
||||||
|
typename basic_string_view<Char>::const_iterator start_it,
|
||||||
|
typename basic_string_view<Char>::const_iterator last_it)
|
||||||
|
{
|
||||||
|
const basic_string_view<Char> view{start_it, last_it};
|
||||||
|
const std::size_t range_size = last_it - start_it;
|
||||||
|
return start_it + std::min(view.find_first_of(v), range_size);
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
constexpr auto find_first_of_it(const basic_string_view<Char>& str, Char v){
|
||||||
|
return find_first_of_it(v, str.begin(), str.end());
|
||||||
|
}
|
||||||
|
template<class It>
|
||||||
|
constexpr bool check_duplicate_char(It start, It end){
|
||||||
|
if(start == end){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto next = start + 1;
|
||||||
|
return *start == *next;
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
constexpr bool is_a_number(Char c){
|
||||||
|
return (c >= '0' && c <= '9');
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
constexpr bool is_valid_name_char(Char c){
|
||||||
|
return (c >= 'a' && c <= 'z') ||
|
||||||
|
(c >= 'A' && c <= 'Z') ||
|
||||||
|
(c == '_');
|
||||||
|
}
|
||||||
|
template<class It>
|
||||||
|
constexpr int to_integer(It start, It end){
|
||||||
|
int retval = 0;
|
||||||
|
std::size_t mul = 1;
|
||||||
|
for(auto it = end;it != start;--it){
|
||||||
|
const auto cur = it-1;
|
||||||
|
retval += ((*cur - '0') * mul);
|
||||||
|
mul *= 10;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
constexpr long long dynamic_integer_retriever::operator()(T&& t){
|
||||||
|
if constexpr(std::is_integral_v<std::remove_cvref_t<T>>){
|
||||||
|
return static_cast<long long>(t);
|
||||||
|
}else{
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid dynamic specifier");
|
||||||
|
return static_cast<long long>(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<class Specs>
|
||||||
|
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(void){
|
||||||
|
return specs.on_dynamic_width();
|
||||||
|
}
|
||||||
|
template<class Specs>
|
||||||
|
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(int i){
|
||||||
|
return specs.on_dynamic_width(i);
|
||||||
|
}
|
||||||
|
template<class Specs>
|
||||||
|
template<class It>
|
||||||
|
constexpr decltype(auto) dynamic_width_adapter<Specs>::operator()(It start, It last){
|
||||||
|
return specs.on_dynamic_width(start, last);
|
||||||
|
}
|
||||||
|
template<class Specs>
|
||||||
|
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(void){
|
||||||
|
return specs.on_dynamic_precision();
|
||||||
|
}
|
||||||
|
template<class Specs>
|
||||||
|
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(int i){
|
||||||
|
return specs.on_dynamic_precision(i);
|
||||||
|
}
|
||||||
|
template<class Specs>
|
||||||
|
template<class It>
|
||||||
|
constexpr decltype(auto) dynamic_precision_adapter<Specs>::operator()(It start, It last){
|
||||||
|
return specs.on_dynamic_precision(start, last);
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(void){
|
||||||
|
return (id = handler.on_arg_id());
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(int i){
|
||||||
|
id = i;
|
||||||
|
return handler.on_arg_id(i);
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
template<class It>
|
||||||
|
constexpr decltype(auto) dynamic_index_adapter<Handler>::operator()(It start, It last){
|
||||||
|
id = handler.on_arg_id(start, last);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_fill_align_option(It start, It last, FormatSpecs&& specs){
|
||||||
|
char fill_val = ' ';
|
||||||
|
It it = start;
|
||||||
|
for(int i = 0;i < 2 && it != last;++i){
|
||||||
|
if(*it == '>' || *it == '<' || *it == '^'){
|
||||||
|
specs.on_align(*it, fill_val);
|
||||||
|
return ++it;
|
||||||
|
}
|
||||||
|
fill_val = *it;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_sign_option(It start, It last, FormatSpecs&& specs){
|
||||||
|
if(*start == '+' || *start == '-' || *start == ' '){
|
||||||
|
specs.on_sign(*start);
|
||||||
|
return ++start;
|
||||||
|
}
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_alt_form_option(It start, It last, FormatSpecs&& specs){
|
||||||
|
if(*start == '#'){
|
||||||
|
specs.on_alt_form();
|
||||||
|
++start;
|
||||||
|
}
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_zero_fill_option(It start, It last, FormatSpecs&& specs){
|
||||||
|
if(*start == '0'){
|
||||||
|
specs.on_zero_fill();
|
||||||
|
++start;
|
||||||
|
}
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
template<class It, class Adapter>
|
||||||
|
constexpr It perform_index_retrieve(It start, It last, Adapter&& func){
|
||||||
|
if(*start == '0'){
|
||||||
|
func(0);
|
||||||
|
return start+1;
|
||||||
|
}
|
||||||
|
if(*start == '}' || *start == ':'){
|
||||||
|
func();
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
if(!is_a_number(*start)){
|
||||||
|
auto it = start;
|
||||||
|
for(;it != last;++it){
|
||||||
|
if(!is_valid_name_char(*it)){
|
||||||
|
func(start, it);
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return last;
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid index spec");
|
||||||
|
}
|
||||||
|
for(auto it = start+1;it != last;++it){
|
||||||
|
if(!is_a_number(*it)){
|
||||||
|
func(to_integer(start, it));
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
template<class It, class Adapter>
|
||||||
|
constexpr It perform_standard_nested_replacement_field(It start, It last, Adapter&& func){
|
||||||
|
auto it = perform_index_retrieve(start, last, func);
|
||||||
|
if(*it != '}'){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Dynamic index invalid spec");
|
||||||
|
}
|
||||||
|
return it+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_width_option(It start, It last, FormatSpecs&& specs){
|
||||||
|
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
|
||||||
|
if(*start == '{'){
|
||||||
|
return perform_standard_nested_replacement_field(start+1, last, dynamic_width_adapter<specs_t>{specs});
|
||||||
|
}
|
||||||
|
if(*start == '0'){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Field invalid spec");
|
||||||
|
}
|
||||||
|
It it = start;
|
||||||
|
for(;is_a_number(*it) && it != last;++it){}
|
||||||
|
if(it == start){
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
specs.on_width(to_integer(start, it));
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_precision_option(It start, It last, FormatSpecs&& specs){
|
||||||
|
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
|
||||||
|
if(*start != '.'){
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
++start;
|
||||||
|
if(*start == '{'){
|
||||||
|
return perform_standard_nested_replacement_field(start+1, last, dynamic_precision_adapter<specs_t>{specs});
|
||||||
|
}
|
||||||
|
if(*start == '0'){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Field invalid spec");
|
||||||
|
}
|
||||||
|
It it = start;
|
||||||
|
for(;is_a_number(*it) && it != last;++it){}
|
||||||
|
if(it == start){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Field invalid spec");
|
||||||
|
}
|
||||||
|
specs.on_precision(to_integer(start, it));
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_locale_option(It start, It last, FormatSpecs&& specs){
|
||||||
|
if(*start == 'L'){
|
||||||
|
specs.on_locale();
|
||||||
|
return ++start;
|
||||||
|
}
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_type_option(It start, It last, FormatSpecs&& specs){
|
||||||
|
if(*start == '}'){
|
||||||
|
specs.on_type_option();
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
specs.on_type_option(*start);
|
||||||
|
return ++start;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class It, class FormatSpecs>
|
||||||
|
constexpr It perform_standard_parse(It start, It last, FormatSpecs&& specs){
|
||||||
|
using specs_t = std::add_lvalue_reference_t<std::remove_reference_t<FormatSpecs>>;
|
||||||
|
using std_fmt_fn = It(*)(It,It,specs_t);
|
||||||
|
constexpr std_fmt_fn fns[] = {
|
||||||
|
perform_standard_fill_align_option<It,specs_t>,
|
||||||
|
perform_standard_sign_option<It,specs_t>,
|
||||||
|
perform_standard_alt_form_option<It,specs_t>,
|
||||||
|
perform_standard_zero_fill_option<It,specs_t>,
|
||||||
|
perform_standard_width_option<It,specs_t>,
|
||||||
|
perform_standard_precision_option<It,specs_t>,
|
||||||
|
perform_standard_locale_option<It,specs_t>,
|
||||||
|
perform_standard_type_option<It,specs_t>
|
||||||
|
};
|
||||||
|
constexpr int num_std_fns = sizeof(fns) / sizeof(fns[0]);
|
||||||
|
|
||||||
|
for(int i = 0;i < num_std_fns;++i){
|
||||||
|
start = fns[i](start, last, specs);
|
||||||
|
if(start == last){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(*start != '}'){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Missing closing brace for format field");
|
||||||
|
}
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Handler, class It>
|
||||||
|
constexpr It perform_format_index_spec(Handler&& handler, It start, It last, std::size_t& id){
|
||||||
|
dynamic_index_adapter<Handler> adapter{handler, id};
|
||||||
|
const auto it = perform_index_retrieve(start, last, adapter);
|
||||||
|
if(*it != '}' && *it != ':'){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid index spec");
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
template<class Handler, class It>
|
||||||
|
constexpr It perform_format_field_parse(Handler&& handler, It start, It last){
|
||||||
|
if(start == last){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Unmatched brace");
|
||||||
|
}
|
||||||
|
std::size_t index = 0;
|
||||||
|
start = perform_format_index_spec(handler, start, last, index);
|
||||||
|
if(*start == ':'){
|
||||||
|
return handler.do_format_spec(start+1, last, index);
|
||||||
|
}
|
||||||
|
handler.do_empty_format(start, last, index);
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
template<class Handler, class Char>
|
||||||
|
constexpr void perform_parse(Handler&& handler, basic_string_view<Char> fmt){
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
|
auto work_it = fmt.begin();
|
||||||
|
auto open_brace_it = fmt.begin();
|
||||||
|
auto close_brace_it = fmt.begin();
|
||||||
|
|
||||||
|
while(true){
|
||||||
|
open_brace_it = find_first_of_it(char_type{'{'}, work_it, fmt.end());
|
||||||
|
|
||||||
|
while(true){
|
||||||
|
close_brace_it = find_first_of_it(char_type{'}'}, work_it, open_brace_it);
|
||||||
|
if(close_brace_it == open_brace_it){
|
||||||
|
//don't go past open brace because we still need to parse it
|
||||||
|
handler.do_raw(work_it, close_brace_it);
|
||||||
|
work_it = close_brace_it;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(check_duplicate_char(close_brace_it, fmt.end())){
|
||||||
|
++close_brace_it;
|
||||||
|
handler.do_raw(work_it, close_brace_it);
|
||||||
|
//go past closing brace to not double parse it
|
||||||
|
work_it = close_brace_it + 1;
|
||||||
|
}else{
|
||||||
|
REXY_THROW_FORMAT_ERROR("Unmatched brace");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(open_brace_it == fmt.end()){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(check_duplicate_char(open_brace_it, fmt.end())){
|
||||||
|
++open_brace_it;
|
||||||
|
handler.do_raw(work_it, open_brace_it);
|
||||||
|
work_it = open_brace_it + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
work_it = perform_format_field_parse(handler, open_brace_it+1, fmt.end()) + 1;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
61
include/rexy/detail/format/parse_context.hpp
Normal file
61
include/rexy/detail/format/parse_context.hpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_PARSE_CONTEXT_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_PARSE_CONTEXT_HPP
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
|
#include "../../string_view.hpp"
|
||||||
|
|
||||||
|
namespace rexy::fmt{
|
||||||
|
|
||||||
|
//Holds the format string for use in calls to 'formatter<T>::parse'
|
||||||
|
//Additionally keeps tally of the current argument index for automatic replacement indexing or
|
||||||
|
//switches to manual indexing mode when encountering a manually indexed field. Throws error on mode switch.
|
||||||
|
template<class Char>
|
||||||
|
class basic_format_parse_context
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using char_type = Char;
|
||||||
|
using iterator = typename basic_string_view<Char>::const_iterator;
|
||||||
|
using const_iterator = typename basic_string_view<Char>::const_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
basic_string_view<char_type> format;
|
||||||
|
std::size_t arg_count;
|
||||||
|
long long arg_index = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
//fmt is the format specifier, num_args is 'sizeof...(Args)' where 'Args' is the parameter pack of the format call
|
||||||
|
constexpr explicit basic_format_parse_context(basic_string_view<char_type> fmt, std::size_t num_args = 0)noexcept;
|
||||||
|
basic_format_parse_context(const basic_format_parse_context&) = delete;
|
||||||
|
basic_format_parse_context(basic_format_parse_context&&) = delete;
|
||||||
|
basic_format_parse_context& operator=(basic_format_parse_context&&) = delete;
|
||||||
|
basic_format_parse_context& operator=(const basic_format_parse_context&) = delete;
|
||||||
|
|
||||||
|
constexpr const_iterator begin(void)const noexcept;
|
||||||
|
constexpr const_iterator end(void)const noexcept;
|
||||||
|
constexpr void advance_to(const_iterator it);
|
||||||
|
constexpr std::size_t next_arg_id(void);
|
||||||
|
constexpr void check_arg_id(std::size_t id);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
75
include/rexy/detail/format/parse_context.tpp
Normal file
75
include/rexy/detail/format/parse_context.tpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_PARSE_CONTEXT_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_PARSE_CONTEXT_TPP
|
||||||
|
|
||||||
|
#include "parse_context.hpp"
|
||||||
|
|
||||||
|
#include "format_error.hpp"
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include <type_traits> //is_constant_evaluated
|
||||||
|
|
||||||
|
namespace rexy::fmt{
|
||||||
|
|
||||||
|
/////////////////////////////basic_format_parse_context//////////////////////////////
|
||||||
|
template<class Char>
|
||||||
|
constexpr basic_format_parse_context<Char>::basic_format_parse_context(basic_string_view<char_type> fmt, std::size_t num_args)noexcept:
|
||||||
|
format(fmt),
|
||||||
|
arg_count(num_args){}
|
||||||
|
|
||||||
|
template<class Char>
|
||||||
|
constexpr auto basic_format_parse_context<Char>::begin(void)const noexcept -> const_iterator{
|
||||||
|
return format.begin();
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
constexpr auto basic_format_parse_context<Char>::end(void)const noexcept -> const_iterator{
|
||||||
|
return format.end();
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
constexpr void basic_format_parse_context<Char>::advance_to(const_iterator it){
|
||||||
|
const std::size_t diff = it - format.begin();
|
||||||
|
format.remove_prefix(diff);
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
constexpr std::size_t basic_format_parse_context<Char>::next_arg_id(void){
|
||||||
|
if(arg_index < 0){
|
||||||
|
//error, already in manual mode
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid indexing mode switch");
|
||||||
|
}
|
||||||
|
if(std::is_constant_evaluated() && arg_index >= arg_count){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Missing argument");
|
||||||
|
}
|
||||||
|
return arg_index++;
|
||||||
|
}
|
||||||
|
template<class Char>
|
||||||
|
constexpr void basic_format_parse_context<Char>::check_arg_id(std::size_t id){
|
||||||
|
if(arg_index > 0){
|
||||||
|
//error, already in automatic mode
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid indexing mode switch");
|
||||||
|
}
|
||||||
|
if(std::is_constant_evaluated() && id >= arg_count){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Missing argument");
|
||||||
|
}
|
||||||
|
arg_index = detail::invalid_arg_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
129
include/rexy/detail/format/specs_handler.hpp
Normal file
129
include/rexy/detail/format/specs_handler.hpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_SPECS_HANDLER_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_SPECS_HANDLER_HPP
|
||||||
|
|
||||||
|
#include <type_traits> //is_constant_evaluated
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
//Stores standard format-spec information to use later in formatting
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
struct format_specs_handler{
|
||||||
|
ParseCtx& parse_ctx;
|
||||||
|
FmtCtx& fmt_ctx;
|
||||||
|
format_specs& specs;
|
||||||
|
|
||||||
|
constexpr format_specs_handler(ParseCtx& pc, FmtCtx& fc, format_specs& s):
|
||||||
|
parse_ctx(pc),
|
||||||
|
fmt_ctx(fc),
|
||||||
|
specs(s){}
|
||||||
|
|
||||||
|
constexpr void on_width(int w);
|
||||||
|
constexpr void on_dynamic_width(void);
|
||||||
|
constexpr void on_dynamic_width(int index);
|
||||||
|
template<class It>
|
||||||
|
constexpr void on_dynamic_width(It first, It last);
|
||||||
|
constexpr void on_precision(int p);
|
||||||
|
constexpr void on_dynamic_precision(void);
|
||||||
|
constexpr void on_dynamic_precision(int index);
|
||||||
|
template<class It>
|
||||||
|
constexpr void on_dynamic_precision(It first, It last);
|
||||||
|
constexpr void on_locale(void);
|
||||||
|
constexpr void on_type_option(int o = 0);
|
||||||
|
constexpr void on_align(int al, int val);
|
||||||
|
constexpr void on_sign(int s);
|
||||||
|
constexpr void on_alt_form(void);
|
||||||
|
constexpr void on_zero_fill(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
//Stores standard format-spec information to use later in formatting. Stores indices of dynamic specifiers
|
||||||
|
//to be retrieved later.
|
||||||
|
template<class ParseCtx>
|
||||||
|
struct dynamic_format_specs_handler{
|
||||||
|
using specs_type = dynamic_format_specs<typename ParseCtx::char_type>;
|
||||||
|
ParseCtx& parse_ctx;
|
||||||
|
specs_type& specs;
|
||||||
|
|
||||||
|
constexpr dynamic_format_specs_handler(ParseCtx& pc, specs_type& s);
|
||||||
|
|
||||||
|
constexpr void on_width(int w);
|
||||||
|
constexpr void on_dynamic_width(void);
|
||||||
|
constexpr void on_dynamic_width(int index);
|
||||||
|
template<class It>
|
||||||
|
constexpr void on_dynamic_width(It first, It last);
|
||||||
|
constexpr void on_precision(int p);
|
||||||
|
constexpr void on_dynamic_precision(void);
|
||||||
|
constexpr void on_dynamic_precision(int index);
|
||||||
|
template<class It>
|
||||||
|
constexpr void on_dynamic_precision(It first, It last);
|
||||||
|
constexpr void on_locale(void);
|
||||||
|
constexpr void on_type_option(int o = 0);
|
||||||
|
constexpr void on_align(int al, int val);
|
||||||
|
constexpr void on_sign(int s);
|
||||||
|
constexpr void on_alt_form(void);
|
||||||
|
constexpr void on_zero_fill(void);
|
||||||
|
};
|
||||||
|
//Used during compile time only. Checks dynamic specifiers given types pack as template parameters.
|
||||||
|
template<class ParseCtx, class... Args>
|
||||||
|
struct cx_format_specs_handler : public dynamic_format_specs_handler<ParseCtx>{
|
||||||
|
using char_type = typename ParseCtx::char_type;
|
||||||
|
|
||||||
|
constexpr cx_format_specs_handler(ParseCtx& pc, dynamic_format_specs<char_type>& s):
|
||||||
|
dynamic_format_specs_handler<ParseCtx>(pc, s){
|
||||||
|
if(!std::is_constant_evaluated()){
|
||||||
|
throw 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void on_dynamic_width(void);
|
||||||
|
constexpr void on_dynamic_width(int index);
|
||||||
|
template<class It>
|
||||||
|
constexpr void on_dynamic_width(It first, It last);
|
||||||
|
constexpr void on_dynamic_precision(void);
|
||||||
|
constexpr void on_dynamic_precision(int index);
|
||||||
|
template<class It>
|
||||||
|
constexpr void on_dynamic_precision(It first, It last);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Handler>
|
||||||
|
struct format_specs_checker : public Handler{
|
||||||
|
storage_type arg_type = storage_type::none_t;
|
||||||
|
bool requires_arithmetic_presentation = false;
|
||||||
|
|
||||||
|
constexpr format_specs_checker(const Handler& h, storage_type type);
|
||||||
|
|
||||||
|
constexpr void on_precision(int p);
|
||||||
|
constexpr void on_dynamic_precision(void);
|
||||||
|
constexpr void on_dynamic_precision(int index);
|
||||||
|
template<class It>
|
||||||
|
constexpr void on_dynamic_precision(It first, It last);
|
||||||
|
constexpr void on_locale(void);
|
||||||
|
constexpr void on_sign(int s);
|
||||||
|
constexpr void on_alt_form(void);
|
||||||
|
constexpr void on_zero_fill(void);
|
||||||
|
constexpr void on_type_option(int type = 0);
|
||||||
|
private:
|
||||||
|
constexpr void check_precision(void);
|
||||||
|
constexpr void check_arithmetic_type(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
389
include/rexy/detail/format/specs_handler.tpp
Normal file
389
include/rexy/detail/format/specs_handler.tpp
Normal file
@ -0,0 +1,389 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_SPECS_HANDLER_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_SPECS_HANDLER_TPP
|
||||||
|
|
||||||
|
#include "specs_handler.hpp"
|
||||||
|
|
||||||
|
#include "format_error.hpp"
|
||||||
|
#include "traits.hpp"
|
||||||
|
#include "named_args.hpp"
|
||||||
|
#include "format_args.hpp"
|
||||||
|
|
||||||
|
#include "internal_types.hpp"
|
||||||
|
#include "standard_types.hpp"
|
||||||
|
#include "basic_types.hpp"
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
/////////////////////////////format_specs_handler//////////////////////////////
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_width(int w){
|
||||||
|
specs.width = w;
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(void){
|
||||||
|
const auto index = parse_ctx.next_arg_id();
|
||||||
|
const auto arg = fmt_ctx.arg(index);
|
||||||
|
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
|
||||||
|
on_width(value);
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(int index){
|
||||||
|
parse_ctx.check_arg_id(index);
|
||||||
|
const auto arg = fmt_ctx.arg(index);
|
||||||
|
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
|
||||||
|
on_width(value);
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
template<class It>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_width(It first, It last){
|
||||||
|
on_dynamic_width(fmt_ctx.arg_index(first, last));
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_precision(int p){
|
||||||
|
specs.precision = p;
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(void){
|
||||||
|
const auto index = parse_ctx.next_arg_id();
|
||||||
|
const auto arg = fmt_ctx.arg(index);
|
||||||
|
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
|
||||||
|
on_precision(value);
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(int index){
|
||||||
|
parse_ctx.check_arg_id(index);
|
||||||
|
const auto arg = fmt_ctx.arg(index);
|
||||||
|
const auto value = visit_format_arg(parse::dynamic_integer_retriever{}, arg);
|
||||||
|
on_precision(value);
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
template<class It>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_dynamic_precision(It first, It last){
|
||||||
|
on_dynamic_precision(fmt_ctx.arg_index(first, last));
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_locale(void){
|
||||||
|
specs.locale = true;
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_type_option(int o){
|
||||||
|
specs.type = o;
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_align(int al, int val){
|
||||||
|
specs.align = static_cast<alignment>(al);
|
||||||
|
specs.align_char = val;
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_sign(int s){
|
||||||
|
specs.sign = s;
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_alt_form(void){
|
||||||
|
specs.alt_form = true;
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class FmtCtx>
|
||||||
|
constexpr void format_specs_handler<ParseCtx,FmtCtx>::on_zero_fill(void){
|
||||||
|
specs.zero_fill = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////dynamic_format_specs_handler//////////////////////////////
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr dynamic_format_specs_handler<ParseCtx>::dynamic_format_specs_handler(ParseCtx& pc, dynamic_format_specs<typename ParseCtx::char_type>& s):
|
||||||
|
parse_ctx(pc),
|
||||||
|
specs(s){}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_width(int w){
|
||||||
|
specs.width = w;
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(void){
|
||||||
|
specs.dyn_width.index = parse_ctx.next_arg_id();
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(int index){
|
||||||
|
parse_ctx.check_arg_id(index);
|
||||||
|
specs.dyn_width.index = index;
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
template<class It>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_width(It first, It last){
|
||||||
|
parse_ctx.check_arg_id(0); //just to make sure we aren't switching indexing modes
|
||||||
|
specs.dyn_width.name = {first, last - first};
|
||||||
|
specs.width_type = dyn_type::string;
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_precision(int p){
|
||||||
|
specs.precision = p;
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(void){
|
||||||
|
specs.dyn_precision.index = parse_ctx.next_arg_id();
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(int index){
|
||||||
|
parse_ctx.check_arg_id(index);
|
||||||
|
specs.dyn_precision.index = index;
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
template<class It>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_dynamic_precision(It first, It last){
|
||||||
|
parse_ctx.check_arg_id(0); //just to make sure we aren't switching indexing modes
|
||||||
|
specs.dyn_precision.name = {first, last - first};
|
||||||
|
specs.precision_type = dyn_type::string;
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_locale(void){
|
||||||
|
specs.locale = true;
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_type_option(int o){
|
||||||
|
specs.type = o;
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_align(int al, int val){
|
||||||
|
specs.align = static_cast<alignment>(al);
|
||||||
|
specs.align_char = val;
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_sign(int s){
|
||||||
|
specs.sign = s;
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_alt_form(void){
|
||||||
|
specs.alt_form = true;
|
||||||
|
}
|
||||||
|
template<class ParseCtx>
|
||||||
|
constexpr void dynamic_format_specs_handler<ParseCtx>::on_zero_fill(void){
|
||||||
|
specs.zero_fill = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////cx_format_specs_handler//////////////////////////////
|
||||||
|
template<class ParseCtx, class... Args>
|
||||||
|
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(void){
|
||||||
|
const auto index = this->parse_ctx.next_arg_id();
|
||||||
|
if(!is_dynamic_integer<char_type,Args...>(index)){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic width");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class... Args>
|
||||||
|
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(int index){
|
||||||
|
this->parse_ctx.check_arg_id(index);
|
||||||
|
if(!is_dynamic_integer<char_type,Args...>(index)){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic width");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class... Args>
|
||||||
|
template<class It>
|
||||||
|
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_width(It first, It last){
|
||||||
|
on_dynamic_width(find_static_named_arg_id<It,Args...>(first, last));
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class... Args>
|
||||||
|
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(void){
|
||||||
|
const auto index = this->parse_ctx.next_arg_id();
|
||||||
|
if(!is_dynamic_integer<char_type,Args...>(index)){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic precision");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class... Args>
|
||||||
|
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(int index){
|
||||||
|
this->parse_ctx.check_arg_id(index);
|
||||||
|
if(!is_dynamic_integer<char_type,Args...>(index)){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type given as dynamic precision");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<class ParseCtx, class... Args>
|
||||||
|
template<class It>
|
||||||
|
constexpr void cx_format_specs_handler<ParseCtx,Args...>::on_dynamic_precision(It first, It last){
|
||||||
|
on_dynamic_precision(find_static_named_arg_id<It,Args...>(first, last));
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////format_specs_checker//////////////////////////////
|
||||||
|
template<class Handler>
|
||||||
|
constexpr format_specs_checker<Handler>::format_specs_checker(const Handler& h, storage_type type):
|
||||||
|
Handler(h), arg_type(type){}
|
||||||
|
template<class Handler>
|
||||||
|
constexpr void format_specs_checker<Handler>::on_precision(int p){
|
||||||
|
check_precision();
|
||||||
|
Handler::on_precision(p);
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
constexpr void format_specs_checker<Handler>::on_dynamic_precision(void){
|
||||||
|
check_precision();
|
||||||
|
Handler::on_dynamic_precision();
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
constexpr void format_specs_checker<Handler>::on_dynamic_precision(int index){
|
||||||
|
check_precision();
|
||||||
|
Handler::on_dynamic_precision(index);
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
template<class It>
|
||||||
|
constexpr void format_specs_checker<Handler>::on_dynamic_precision(It first, It last){
|
||||||
|
check_precision();
|
||||||
|
Handler::on_dynamic_precision(first, last);
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
constexpr void format_specs_checker<Handler>::on_locale(void){
|
||||||
|
check_arithmetic_type();
|
||||||
|
Handler::on_locale();
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
constexpr void format_specs_checker<Handler>::on_sign(int s){
|
||||||
|
requires_arithmetic_presentation = true;
|
||||||
|
check_arithmetic_type();
|
||||||
|
Handler::on_sign(s);
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
constexpr void format_specs_checker<Handler>::on_alt_form(void){
|
||||||
|
requires_arithmetic_presentation = true;
|
||||||
|
check_arithmetic_type();
|
||||||
|
Handler::on_alt_form();
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
constexpr void format_specs_checker<Handler>::on_zero_fill(void){
|
||||||
|
requires_arithmetic_presentation = true;
|
||||||
|
check_arithmetic_type();
|
||||||
|
Handler::on_zero_fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Handler>
|
||||||
|
constexpr void format_specs_checker<Handler>::on_type_option(int type){
|
||||||
|
presentation& chosen = this->specs.present;
|
||||||
|
|
||||||
|
switch(type){
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
case 'B':
|
||||||
|
case 'd':
|
||||||
|
case 'o':
|
||||||
|
case 'x':
|
||||||
|
case 'X':
|
||||||
|
chosen = presentation::int_t;
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
chosen = presentation::char_t;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
chosen = presentation::string_t;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
case 'A':
|
||||||
|
case 'e':
|
||||||
|
case 'E':
|
||||||
|
case 'f':
|
||||||
|
case 'F':
|
||||||
|
case 'g':
|
||||||
|
case 'G':
|
||||||
|
chosen = presentation::float_t;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
chosen = presentation::ptr_t;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type specifier");
|
||||||
|
};
|
||||||
|
|
||||||
|
switch(arg_type){
|
||||||
|
case storage_type::none_t:
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid argument");
|
||||||
|
break;
|
||||||
|
case storage_type::bool_t:
|
||||||
|
if(chosen == presentation::default_t){
|
||||||
|
chosen = presentation::string_t;
|
||||||
|
}
|
||||||
|
if(chosen != presentation::string_t && chosen != presentation::int_t){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type specifier for bool");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case storage_type::char_t:
|
||||||
|
if(chosen == presentation::default_t){
|
||||||
|
chosen = presentation::char_t;
|
||||||
|
}
|
||||||
|
if(chosen != presentation::char_t && chosen != presentation::int_t){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type specifier for character");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case storage_type::int_t:
|
||||||
|
case storage_type::uint_t:
|
||||||
|
case storage_type::long_long_t:
|
||||||
|
case storage_type::ulong_long_t:
|
||||||
|
if(chosen == presentation::default_t){
|
||||||
|
chosen = presentation::int_t;
|
||||||
|
}
|
||||||
|
if(chosen != presentation::int_t && chosen != presentation::char_t){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type specifier for integral");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case storage_type::float_t:
|
||||||
|
case storage_type::double_t:
|
||||||
|
case storage_type::long_double_t:
|
||||||
|
if(chosen == presentation::default_t){
|
||||||
|
chosen = presentation::float_t;
|
||||||
|
}
|
||||||
|
if(chosen != presentation::float_t){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type specifier for floating point");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case storage_type::char_ptr_t:
|
||||||
|
case storage_type::string_t:
|
||||||
|
if(chosen == presentation::default_t){
|
||||||
|
chosen = presentation::string_t;
|
||||||
|
}
|
||||||
|
if(chosen != presentation::string_t){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type specifier for string");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case storage_type::ptr_t:
|
||||||
|
if(chosen == presentation::default_t){
|
||||||
|
chosen = presentation::ptr_t;
|
||||||
|
}
|
||||||
|
if(chosen != presentation::ptr_t){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type specifier for pointer");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case storage_type::custom_t:
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
if(requires_arithmetic_presentation && chosen != presentation::int_t && chosen != presentation::float_t){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Invalid type specifier with modifier requiring integer presentation");
|
||||||
|
}
|
||||||
|
return Handler::on_type_option(type);
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
constexpr void format_specs_checker<Handler>::check_precision(void){
|
||||||
|
if(!(is_floating_storage_type(arg_type) || is_string_storage_type(arg_type))){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Precision not valid for argument type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<class Handler>
|
||||||
|
constexpr void format_specs_checker<Handler>::check_arithmetic_type(void){
|
||||||
|
if(!is_integral_storage_type(arg_type)){
|
||||||
|
REXY_THROW_FORMAT_ERROR("Formatting argument requires an arithmetic type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
38
include/rexy/detail/format/standard_types.hpp
Normal file
38
include/rexy/detail/format/standard_types.hpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_STANDARD_TYPES_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_STANDARD_TYPES_HPP
|
||||||
|
|
||||||
|
namespace rexy::fmt{
|
||||||
|
|
||||||
|
template<class T, class Char>
|
||||||
|
class formatter;
|
||||||
|
template<class Context>
|
||||||
|
class basic_format_arg;
|
||||||
|
template<class Context>
|
||||||
|
class basic_format_args;
|
||||||
|
template<class OutIt, class Char>
|
||||||
|
class basic_format_context;
|
||||||
|
template<class Char>
|
||||||
|
class basic_format_parse_context;
|
||||||
|
struct format_error;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
71
include/rexy/detail/format/storage.hpp
Normal file
71
include/rexy/detail/format/storage.hpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_STORAGE_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_STORAGE_HPP
|
||||||
|
|
||||||
|
#include <type_traits> //declval
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
//tag for use in basic_format_arg(s)
|
||||||
|
enum class storage_type{
|
||||||
|
none_t = 0,
|
||||||
|
|
||||||
|
bool_t,
|
||||||
|
char_t,
|
||||||
|
int_t,
|
||||||
|
uint_t,
|
||||||
|
long_long_t,
|
||||||
|
ulong_long_t,
|
||||||
|
float_t,
|
||||||
|
double_t,
|
||||||
|
long_double_t,
|
||||||
|
|
||||||
|
char_ptr_t,
|
||||||
|
string_t,
|
||||||
|
ptr_t,
|
||||||
|
|
||||||
|
custom_t
|
||||||
|
};
|
||||||
|
constexpr bool is_integral_storage_type(storage_type t){
|
||||||
|
return t >= storage_type::bool_t && t <= storage_type::long_double_t;
|
||||||
|
}
|
||||||
|
constexpr bool is_integer_storage_type(storage_type t){
|
||||||
|
return t >= storage_type::bool_t && t <= storage_type::ulong_long_t;
|
||||||
|
}
|
||||||
|
constexpr bool is_floating_storage_type(storage_type t){
|
||||||
|
return t >= storage_type::float_t && t <= storage_type::long_double_t;
|
||||||
|
}
|
||||||
|
constexpr bool is_string_storage_type(storage_type t){
|
||||||
|
return t == storage_type::string_t || t == storage_type::char_ptr_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Mappings from actual type to the type used in basic_format_arg(s)
|
||||||
|
template<class T, class Char>
|
||||||
|
consteval storage_type map_to_storage_enum(void);
|
||||||
|
template<class T, class Char>
|
||||||
|
static constexpr storage_type map_to_storage_enum_v = map_to_storage_enum<T,Char>();
|
||||||
|
template<class Context>
|
||||||
|
struct map_to_stored_type_helper;
|
||||||
|
template<class T, class Context>
|
||||||
|
using stored_type_t = decltype(std::declval<map_to_stored_type_helper<Context>>()(std::declval<T>()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
111
include/rexy/detail/format/storage.tpp
Normal file
111
include/rexy/detail/format/storage.tpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_STORAGE_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_STORAGE_TPP
|
||||||
|
|
||||||
|
#include "storage.hpp"
|
||||||
|
|
||||||
|
#include "named_args.hpp"
|
||||||
|
|
||||||
|
#include <type_traits> //is_pointer, remove_cvref, remove_pointer, is_same, is_arithmetic
|
||||||
|
#include <concepts> //unsigned_integral, signed_integral, floating_point
|
||||||
|
#include <cstddef> //nullptr_t
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
///////////////////////storage type mapping////////////////////////
|
||||||
|
template<class T, class Char>
|
||||||
|
consteval storage_type map_to_storage_enum(void){
|
||||||
|
using type = std::remove_cvref_t<T>;
|
||||||
|
if constexpr(std::is_pointer_v<type>){
|
||||||
|
if constexpr(std::is_same_v<std::remove_cv_t<std::remove_pointer_t<type>>,Char>){
|
||||||
|
return storage_type::char_ptr_t;
|
||||||
|
}else{
|
||||||
|
return storage_type::ptr_t;
|
||||||
|
}
|
||||||
|
}else if constexpr(std::is_same_v<type,basic_string_view<Char>>){
|
||||||
|
return storage_type::string_t;
|
||||||
|
}else if constexpr(std::is_same_v<type,Char>){
|
||||||
|
return storage_type::char_t;
|
||||||
|
}else if constexpr(std::is_same_v<type,std::monostate>){
|
||||||
|
return storage_type::none_t;
|
||||||
|
}else if constexpr(std::is_same_v<type,bool>){
|
||||||
|
return storage_type::bool_t;
|
||||||
|
}else if constexpr(std::is_same_v<type,int>){
|
||||||
|
return storage_type::int_t;
|
||||||
|
}else if constexpr(std::is_same_v<type,unsigned int>){
|
||||||
|
return storage_type::uint_t;
|
||||||
|
}else if constexpr(std::is_same_v<type,long long>){
|
||||||
|
return storage_type::long_long_t;
|
||||||
|
}else if constexpr(std::is_same_v<type,unsigned long long>){
|
||||||
|
return storage_type::ulong_long_t;
|
||||||
|
}else if constexpr(std::is_same_v<type,float>){
|
||||||
|
return storage_type::float_t;
|
||||||
|
}else if constexpr(std::is_same_v<type,double>){
|
||||||
|
return storage_type::double_t;
|
||||||
|
}else if constexpr(std::is_same_v<type,long double>){
|
||||||
|
return storage_type::long_double_t;
|
||||||
|
}else{
|
||||||
|
return storage_type::custom_t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Context>
|
||||||
|
struct map_to_stored_type_helper{
|
||||||
|
|
||||||
|
using char_type = typename Context::char_type;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
requires (!std::is_arithmetic_v<T> && !std::is_pointer_v<T> && !NamedArg<T>)
|
||||||
|
constexpr auto operator()(T) -> typename basic_format_arg<Context>::handle;
|
||||||
|
constexpr auto operator()(std::unsigned_integral auto i){
|
||||||
|
if constexpr(sizeof(i) <= sizeof(unsigned int)){
|
||||||
|
return (unsigned int){};
|
||||||
|
}else{
|
||||||
|
return (unsigned long long){};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
constexpr auto operator()(std::signed_integral auto i){
|
||||||
|
if constexpr(sizeof(i) <= sizeof(int)){
|
||||||
|
return int{};
|
||||||
|
}else{
|
||||||
|
return (long long){};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<std::floating_point T>
|
||||||
|
constexpr auto operator()(T) -> std::remove_cvref_t<T>;
|
||||||
|
constexpr auto operator()(char_type) -> char_type;
|
||||||
|
constexpr auto operator()(bool) -> bool;
|
||||||
|
constexpr auto operator()(float) -> float;
|
||||||
|
constexpr auto operator()(double) -> double;
|
||||||
|
constexpr auto operator()(long double) -> long double;
|
||||||
|
constexpr auto operator()(const char_type*) -> const char_type*;
|
||||||
|
constexpr auto operator()(basic_string_view<char_type>) -> basic_string_view<char_type>;
|
||||||
|
constexpr auto operator()(basic_string<char_type>) -> basic_string_view<char_type>;
|
||||||
|
template<class T>
|
||||||
|
requires (!std::is_same_v<std::remove_cvref_t<T>,char_type>)
|
||||||
|
constexpr auto operator()(T*) -> const void*;
|
||||||
|
constexpr auto operator()(std::nullptr_t) -> const void*;
|
||||||
|
template<NamedArg T>
|
||||||
|
constexpr auto operator()(T t) -> decltype((*this)(t.value));
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
126
include/rexy/detail/format/traits.hpp
Normal file
126
include/rexy/detail/format/traits.hpp
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_TRAITS_HPP
|
||||||
|
#define REXY_DETAIL_FORMAT_TRAITS_HPP
|
||||||
|
|
||||||
|
#include <type_traits> //is_convertible, is_floating_point, integral_constant, is_same, remove_cvref
|
||||||
|
#include <concepts> //integral, floating_point
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
|
#include "standard_types.hpp"
|
||||||
|
#include "storage.hpp"
|
||||||
|
#include "named_args.hpp"
|
||||||
|
#include "internal_types.hpp"
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct valid_dynamic_index{
|
||||||
|
static constexpr bool value = std::is_convertible_v<T,long long> && !std::is_floating_point_v<T>;
|
||||||
|
};
|
||||||
|
template<class T>
|
||||||
|
static constexpr bool valid_dynamic_index_v = valid_dynamic_index<T>::value;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct is_char_or_bool : public std::false_type{};
|
||||||
|
template<>
|
||||||
|
struct is_char_or_bool<bool> : public std::true_type{};
|
||||||
|
template<>
|
||||||
|
struct is_char_or_bool<char> : public std::true_type{};
|
||||||
|
template<>
|
||||||
|
struct is_char_or_bool<wchar_t> : public std::true_type{};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
concept Integral = std::integral<T> && !is_char_or_bool<T>::value;
|
||||||
|
template<class T>
|
||||||
|
concept Bool = std::is_same_v<std::remove_cvref_t<T>,bool>;
|
||||||
|
template<class T>
|
||||||
|
concept Floating = std::floating_point<T>;
|
||||||
|
template<class T>
|
||||||
|
concept Arithmetic = Integral<T> || Floating<T>;
|
||||||
|
template<class T, class FmtCtx>
|
||||||
|
concept Handle = std::is_same_v<std::remove_cvref_t<T>,typename basic_format_arg<FmtCtx>::handle>;
|
||||||
|
template<class T>
|
||||||
|
concept Formatter_Char = std::is_same_v<T,char> || std::is_same_v<T,wchar_t>;
|
||||||
|
template<class T, class FmtCtx>
|
||||||
|
struct is_handle{
|
||||||
|
static constexpr bool value = Handle<T,FmtCtx>;
|
||||||
|
};
|
||||||
|
template<class T, class FmtCtx>
|
||||||
|
static constexpr bool is_handle_v = is_handle<T,FmtCtx>::value;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct extract_char_type_from_context;
|
||||||
|
template<class OutIt, class Char>
|
||||||
|
struct extract_char_type_from_context<basic_format_context<OutIt,Char>>{
|
||||||
|
using type = Char;
|
||||||
|
};
|
||||||
|
template<class T>
|
||||||
|
using extract_char_type_from_context_t = typename extract_char_type_from_context<T>::type;
|
||||||
|
|
||||||
|
|
||||||
|
template<std::size_t I, class FmtCtx, class Arg, class... Args>
|
||||||
|
constexpr bool is_dynamic_integer_impl(int index){
|
||||||
|
using mapped_t = stored_type_t<Arg,FmtCtx>;
|
||||||
|
if(index == I){
|
||||||
|
return valid_dynamic_index_v<mapped_t>;
|
||||||
|
}
|
||||||
|
if constexpr(sizeof...(Args) > 0){
|
||||||
|
return is_dynamic_integer_impl<I+1,FmtCtx,Args...>(index);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
template<class Char, class... Args>
|
||||||
|
constexpr bool is_dynamic_integer(int index){
|
||||||
|
using fmt_ctx_t = fmt_context_t<Char>;
|
||||||
|
if constexpr(sizeof...(Args) == 0){
|
||||||
|
return false;
|
||||||
|
}else{
|
||||||
|
return is_dynamic_integer_impl<0,fmt_ctx_t,Args...>(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr unsigned char utf_test[] = "\u00B5";
|
||||||
|
constexpr bool is_utf8(void){
|
||||||
|
using uchar = unsigned char;
|
||||||
|
return (sizeof(utf_test) == 3) && (uchar(utf_test[0]) == 0xC2) && (uchar(utf_test[1]) == 0xB5);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct is_utf8_encoded_string_type{
|
||||||
|
static constexpr bool value = false;
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct is_utf8_encoded_string_type<char8_t>{
|
||||||
|
static constexpr bool value = true;
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct is_utf8_encoded_string_type<char>{
|
||||||
|
static constexpr bool value = is_utf8();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
static constexpr bool is_utf8_encoded_string_type_v = is_utf8_encoded_string_type<T>::value;
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
concept UTF8_String = is_utf8_encoded_string_type_v<T>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
91
include/rexy/detail/format/utf_iterator.hpp
Normal file
91
include/rexy/detail/format/utf_iterator.hpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_DETAIL_FORMAT_UTF_ITERATOR_TPP
|
||||||
|
#define REXY_DETAIL_FORMAT_UTF_ITERATOR_TPP
|
||||||
|
|
||||||
|
namespace rexy::fmt::detail{
|
||||||
|
|
||||||
|
template<class Char>
|
||||||
|
class utf8_iterator
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
const Char* m_start = nullptr;
|
||||||
|
const Char* m_last = nullptr;
|
||||||
|
const Char* m_next = nullptr;
|
||||||
|
char32_t m_value = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr utf8_iterator(const Char* first, const Char* last):
|
||||||
|
m_start(first), m_last(last)
|
||||||
|
{
|
||||||
|
m_next = convert_codepoint(m_start, m_last, m_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr utf8_iterator& operator++(void){
|
||||||
|
m_start = m_next;
|
||||||
|
if(m_start != m_last){
|
||||||
|
m_next = convert_codepoint(m_start, m_last, m_value);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
constexpr utf8_iterator operator++(int){
|
||||||
|
auto tmp = *this;
|
||||||
|
++(*this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
constexpr char32_t operator*(void)const{
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
constexpr bool valid(void)const{
|
||||||
|
return m_start != m_last;
|
||||||
|
}
|
||||||
|
constexpr std::size_t byte_count(void)const{
|
||||||
|
return m_next - m_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr const Char* convert_codepoint(const Char* first, const Char* last, char32_t& codepoint){
|
||||||
|
const std::size_t maxlen = last - first;
|
||||||
|
if((*first & 0x80) == 0){
|
||||||
|
codepoint = first[0];
|
||||||
|
return first + 1;
|
||||||
|
}else if((*first & 0xE0) == 0xC0 && maxlen > 1){
|
||||||
|
codepoint = ((first[0] & 0x1F) << 6);
|
||||||
|
codepoint += (first[1] & 0x3F);
|
||||||
|
return first + 2;
|
||||||
|
}else if((*first & 0xF0) == 0xE0 && maxlen > 2){
|
||||||
|
codepoint = ((first[0] & 0x0F) << 12);
|
||||||
|
codepoint += ((first[1] & 0x3F) << 6);
|
||||||
|
codepoint += (first[2] & 0x3F);
|
||||||
|
return first + 3;
|
||||||
|
}else if((*first & 0xF8) == 0xF0 && maxlen > 3){
|
||||||
|
codepoint = ((first[0] & 0x07) << 18);
|
||||||
|
codepoint += ((first[1] & 0x3F) << 12);
|
||||||
|
codepoint += (first[2] & 0x3F) << 6;
|
||||||
|
codepoint += (first[3] & 0x3F);
|
||||||
|
return first + 4;
|
||||||
|
}
|
||||||
|
codepoint = 0;
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
147
include/rexy/format.hpp
Normal file
147
include/rexy/format.hpp
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_FORMAT_HPP
|
||||||
|
#define REXY_FORMAT_HPP
|
||||||
|
|
||||||
|
#include "string.hpp"
|
||||||
|
#include "string_view.hpp"
|
||||||
|
|
||||||
|
#include "detail/format/standard_types.hpp"
|
||||||
|
#include "detail/format/internal_types.hpp"
|
||||||
|
#include "detail/format/arg_store.hpp"
|
||||||
|
#include "detail/format/format_string.hpp"
|
||||||
|
#include "detail/format/format_args.hpp"
|
||||||
|
|
||||||
|
#include <cstdio> //FILE
|
||||||
|
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
#include <locale> //locale
|
||||||
|
|
||||||
|
namespace rexy{
|
||||||
|
|
||||||
|
//Alias declarations of standard defined types
|
||||||
|
using format_context = fmt::detail::fmt_context_t<char>;
|
||||||
|
using wformat_context = fmt::detail::fmt_context_t<wchar_t>;
|
||||||
|
using parse_context = fmt::detail::parse_context_t<char>;
|
||||||
|
using wparse_context = fmt::detail::parse_context_t<wchar_t>;
|
||||||
|
using format_arg = fmt::basic_format_arg<format_context>;
|
||||||
|
using wformat_arg = fmt::basic_format_arg<wformat_context>;
|
||||||
|
using format_args = fmt::basic_format_args<format_context>;
|
||||||
|
using wformat_args = fmt::basic_format_args<wformat_context>;
|
||||||
|
|
||||||
|
template<class Context = format_context, class... Args>
|
||||||
|
fmt::detail::basic_format_arg_store<Context,Args...> make_format_args(Args&&... args);
|
||||||
|
template<class Context = wformat_context, class... Args>
|
||||||
|
fmt::detail::basic_format_arg_store<Context,Args...> make_wformat_args(Args&&... args);
|
||||||
|
|
||||||
|
template<class OutIt>
|
||||||
|
OutIt vformat_to(OutIt out, string_view fmt, format_args args);
|
||||||
|
template<class OutIt>
|
||||||
|
OutIt vformat_to(OutIt out, const std::locale& loc, string_view fmt, wformat_args args);
|
||||||
|
|
||||||
|
string vformat(string_view fmt, format_args args);
|
||||||
|
string vformat(const std::locale& loc, string_view fmt, format_args args);
|
||||||
|
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
OutIt format_to(OutIt out, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
string format(fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
template<class... Args>
|
||||||
|
string format(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t formatted_size(fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t formatted_size(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class OutIt>
|
||||||
|
OutIt vformat_to(OutIt out, wstring_view fmt, wformat_args args);
|
||||||
|
template<class OutIt>
|
||||||
|
OutIt vformat_to(OutIt out, const std::locale& loc, wstring_view fmt, wformat_args args);
|
||||||
|
|
||||||
|
wstring vformat(wstring_view fmt, format_args args);
|
||||||
|
wstring vformat(const std::locale& loc, wstring_view fmt, wformat_args args);
|
||||||
|
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
OutIt format_to(OutIt out, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||||
|
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
wstring format(fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||||
|
template<class... Args>
|
||||||
|
wstring format(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t formatted_size(fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t formatted_size(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t print(fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t print(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t println(fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t println(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args);
|
||||||
|
|
||||||
|
std::size_t vprint_unicode(string_view fmt, format_args args);
|
||||||
|
std::size_t vprint_unicode(FILE* stream, string_view fmt, format_args args);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
template<class T, class Char>
|
||||||
|
constexpr auto arg(const Char* name, T&& t);
|
||||||
|
template<class T, class Char>
|
||||||
|
constexpr auto arg(rexy::basic_string_view<Char> name, T&& t);
|
||||||
|
template<rexy::cx::string Name, class T>
|
||||||
|
constexpr auto arg(T&& t);
|
||||||
|
|
||||||
|
inline namespace fmt_literals{
|
||||||
|
template<rexy::cx::string Name>
|
||||||
|
constexpr auto operator""_a(void);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#include "format.tpp"
|
||||||
|
|
||||||
|
#endif
|
||||||
338
include/rexy/format.tpp
Normal file
338
include/rexy/format.tpp
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
/**
|
||||||
|
This file is a part of rexy's general purpose library
|
||||||
|
Copyright (C) 2022 rexy712
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REXY_FORMAT_TPP
|
||||||
|
#define REXY_FORMAT_TPP
|
||||||
|
|
||||||
|
#include "detail/format/format_args.tpp"
|
||||||
|
#include "detail/format/formatter.tpp"
|
||||||
|
#include "detail/format/storage.tpp"
|
||||||
|
#include "detail/format/format_string.tpp"
|
||||||
|
#include "detail/format/arg_store.tpp"
|
||||||
|
#include "detail/format/output_buffer.tpp"
|
||||||
|
#include "detail/format/basic_types.tpp"
|
||||||
|
#include "detail/format/specs_handler.tpp"
|
||||||
|
#include "detail/format/parse.tpp"
|
||||||
|
#include "detail/format/context_handler.tpp"
|
||||||
|
#include "detail/format/named_args.tpp"
|
||||||
|
#include "detail/format/parse_context.tpp"
|
||||||
|
#include "detail/format/format_context.tpp"
|
||||||
|
|
||||||
|
#include <iterator> //back_insert_iterator
|
||||||
|
#include <utility> //move, forward
|
||||||
|
#include <type_traits> //is_same
|
||||||
|
#include <locale> //locale
|
||||||
|
#include <cstddef> //size_t
|
||||||
|
|
||||||
|
namespace rexy{
|
||||||
|
|
||||||
|
namespace fmt::detail{
|
||||||
|
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
OutIt vformat_to(OutIt out, basic_string_view<std::type_identity_t<Char>> fmt, impl_format_args<std::type_identity_t<Char>> args){
|
||||||
|
using char_type = Char;
|
||||||
|
using format_handler_t = fmt::detail::format_handler<char_type>;
|
||||||
|
|
||||||
|
if constexpr(std::is_same_v<OutIt,fmt::detail::output_iterator_t<char_type>>){
|
||||||
|
|
||||||
|
fmt::detail::format_handler<char_type> handler{out, fmt, args};
|
||||||
|
fmt::detail::parse::perform_parse(handler, fmt);
|
||||||
|
|
||||||
|
return std::move(out);
|
||||||
|
}else{
|
||||||
|
|
||||||
|
fmt::detail::basic_format_output_buffer<char_type,OutIt> outbuf{out};
|
||||||
|
fmt::detail::format_handler<char_type> handler{fmt::detail::output_iterator_t<char_type>{outbuf}, fmt, args};
|
||||||
|
|
||||||
|
fmt::detail::parse::perform_parse(handler, fmt);
|
||||||
|
|
||||||
|
return std::move(outbuf.out());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
template<class Char, class OutIt>
|
||||||
|
OutIt vformat_to(OutIt out, const std::locale& loc, basic_string_view<std::type_identity_t<Char>> fmt, impl_format_args<std::type_identity_t<Char>> args){
|
||||||
|
using char_type = Char;
|
||||||
|
using format_handler_t = fmt::detail::format_handler<char_type>;
|
||||||
|
|
||||||
|
if constexpr(std::is_same_v<OutIt,fmt::detail::output_iterator_t<char_type>>){
|
||||||
|
|
||||||
|
fmt::detail::format_handler<char_type> handler{out, fmt, args, loc};
|
||||||
|
fmt::detail::parse::perform_parse(handler, fmt);
|
||||||
|
|
||||||
|
return std::move(out);
|
||||||
|
}else{
|
||||||
|
|
||||||
|
fmt::detail::basic_format_output_buffer<char_type,OutIt> outbuf{out};
|
||||||
|
fmt::detail::format_handler<char_type> handler{fmt::detail::output_iterator_t<char_type>{outbuf}, fmt, args, loc};
|
||||||
|
|
||||||
|
fmt::detail::parse::perform_parse(handler, fmt);
|
||||||
|
|
||||||
|
return std::move(outbuf.out());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class Char, class... Args>
|
||||||
|
basic_format_arg_store<fmt_context_t<Char>,Args...> make_format_args(Args&&... args){
|
||||||
|
return basic_format_arg_store<fmt_context_t<Char>,Args...>{std::forward<Args>(args)...};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Context, class... Args>
|
||||||
|
fmt::detail::basic_format_arg_store<Context,Args...> make_format_args(Args&&... args){
|
||||||
|
return fmt::detail::basic_format_arg_store<Context,Args...>{std::forward<Args>(args)...};
|
||||||
|
}
|
||||||
|
template<class Context, class... Args>
|
||||||
|
fmt::detail::basic_format_arg_store<Context,Args...> make_wformat_args(Args&&... args){
|
||||||
|
return fmt::detail::basic_format_arg_store<Context,Args...>{std::forward<Args>(args)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////char overloads///////////////////////////////////////////////
|
||||||
|
template<class OutIt>
|
||||||
|
OutIt vformat_to(OutIt out, string_view fmt, format_args args){
|
||||||
|
return fmt::detail::vformat_to<char>(std::move(out), fmt, std::move(args));
|
||||||
|
}
|
||||||
|
template<class OutIt>
|
||||||
|
OutIt vformat_to(OutIt out, const std::locale& loc, string_view fmt, format_args args){
|
||||||
|
return fmt::detail::vformat_to<char>(std::move(out), loc, fmt, std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
string vformat(string_view fmt, format_args args){
|
||||||
|
using outit_t = std::back_insert_iterator<string>;
|
||||||
|
string output;
|
||||||
|
outit_t out{output};
|
||||||
|
vformat_to(std::move(out), fmt, std::move(args));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
string vformat(const std::locale& loc, string_view fmt, format_args args){
|
||||||
|
using outit_t = std::back_insert_iterator<string>;
|
||||||
|
string output;
|
||||||
|
outit_t out{output};
|
||||||
|
vformat_to(std::move(out), loc, fmt, std::move(args));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
OutIt format_to(OutIt out, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
return vformat_to(std::move(out), fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
return vformat_to(std::move(out), loc, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
fmt::detail::basic_format_output_n_buffer<char,OutIt> outbuf{std::move(out), max};
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<char>{outbuf}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
return {outbuf.out(), outbuf.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
fmt::detail::basic_format_output_n_buffer<char,OutIt> outbuf{std::move(out), max};
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<char>{outbuf}, loc, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
return {outbuf.out(), outbuf.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
string format(fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
using outit_t = std::back_insert_iterator<string>;
|
||||||
|
string output;
|
||||||
|
outit_t out{output};
|
||||||
|
vformat_to(std::move(out), fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
string format(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
using outit_t = std::back_insert_iterator<string>;
|
||||||
|
string output;
|
||||||
|
outit_t out{output};
|
||||||
|
vformat_to(std::move(out), loc, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t formatted_size(fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
fmt::detail::basic_format_size_buffer<char> counter;
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<char>{counter}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
return counter.count();
|
||||||
|
}
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t formatted_size(const std::locale& loc, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
fmt::detail::basic_format_size_buffer<char> counter;
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<char>{counter}, loc, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
return counter.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////wchar_t overloads///////////////////////////////////////////////
|
||||||
|
template<class OutIt>
|
||||||
|
OutIt vformat_to(OutIt out, wstring_view fmt, wformat_args args){
|
||||||
|
return fmt::detail::vformat_to<wchar_t>(std::move(out), fmt, std::move(args));
|
||||||
|
}
|
||||||
|
template<class OutIt>
|
||||||
|
OutIt vformat_to(OutIt out, const std::locale& loc, wstring_view fmt, wformat_args args){
|
||||||
|
return fmt::detail::vformat_to<wchar_t>(std::move(out), loc, fmt, std::move(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
wstring vformat(wstring_view fmt, wformat_args args){
|
||||||
|
using outit_t = std::back_insert_iterator<wstring>;
|
||||||
|
wstring output;
|
||||||
|
outit_t out{output};
|
||||||
|
vformat_to(std::move(out), fmt, std::move(args));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
wstring vformat(const std::locale& loc, wstring_view fmt, wformat_args args){
|
||||||
|
using outit_t = std::back_insert_iterator<wstring>;
|
||||||
|
wstring output;
|
||||||
|
outit_t out{output};
|
||||||
|
vformat_to(std::move(out), loc, fmt, std::move(args));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
OutIt format_to(OutIt out, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||||
|
return vformat_to(std::move(out), fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
OutIt format_to(OutIt out, const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||||
|
return vformat_to(std::move(out), loc, fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
format_to_n_result<OutIt> format_to_n(OutIt out, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||||
|
fmt::detail::basic_format_output_n_buffer<wchar_t,OutIt> outbuf{std::move(out), max};
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<wchar_t>{outbuf}, fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||||
|
return {outbuf.out(), outbuf.count()};
|
||||||
|
}
|
||||||
|
template<class OutIt, class... Args>
|
||||||
|
format_to_n_result<OutIt> format_to_n(OutIt out, const std::locale& loc, std::size_t max, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||||
|
fmt::detail::basic_format_output_n_buffer<wchar_t,OutIt> outbuf{std::move(out), max};
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<wchar_t>{outbuf}, loc, fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||||
|
return {outbuf.out(), outbuf.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
wstring format(fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||||
|
using outit_t = std::back_insert_iterator<wstring>;
|
||||||
|
wstring output;
|
||||||
|
outit_t out{output};
|
||||||
|
vformat_to(out, fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
template<class... Args>
|
||||||
|
wstring format(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||||
|
using outit_t = std::back_insert_iterator<wstring>;
|
||||||
|
wstring output;
|
||||||
|
outit_t out{output};
|
||||||
|
vformat_to(out, fmt.str, make_wformat_args(std::forward<Args>(args)...));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t formatted_size(fmt::detail::wformat_string<Args...> fmt, Args&&... args){
|
||||||
|
|
||||||
|
}
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t formatted_size(const std::locale& loc, fmt::detail::wformat_string<Args...> fmt, Args&&... args);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////print char overloads///////////////////////////////////////////////
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t print(fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
fmt::detail::basic_format_output_buffer<char,fmt::detail::print_iterator<char>> out{fmt::detail::print_iterator<char>{stdout}};
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
return out.count();
|
||||||
|
}
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t print(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
fmt::detail::basic_format_output_buffer<char,fmt::detail::print_iterator<char>> out{fmt::detail::print_iterator<char>{stream}};
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
return out.count();
|
||||||
|
}
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t println(fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
fmt::detail::basic_format_output_buffer<char,fmt::detail::print_iterator<char>> out{fmt::detail::print_iterator<char>{stdout}};
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
out.push_back('\n');
|
||||||
|
return out.count();
|
||||||
|
}
|
||||||
|
template<class... Args>
|
||||||
|
std::size_t println(FILE* stream, fmt::detail::format_string<Args...> fmt, Args&&... args){
|
||||||
|
fmt::detail::basic_format_output_buffer<char,fmt::detail::print_iterator<char>> out{fmt::detail::print_iterator<char>{stream}};
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt.str, make_format_args(std::forward<Args>(args)...));
|
||||||
|
out.push_back('\n');
|
||||||
|
return out.count();
|
||||||
|
}
|
||||||
|
std::size_t vprint_unicode(string_view fmt, format_args args){
|
||||||
|
fmt::detail::basic_format_output_buffer<char,fmt::detail::print_iterator<char>> out{fmt::detail::print_iterator<char>{stdout}};
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt, args);
|
||||||
|
return out.count();
|
||||||
|
}
|
||||||
|
std::size_t vprint_unicode(FILE* stream, string_view fmt, format_args args){
|
||||||
|
fmt::detail::basic_format_output_buffer<char,fmt::detail::print_iterator<char>> out{fmt::detail::print_iterator<char>{stream}};
|
||||||
|
vformat_to(fmt::detail::output_iterator_t<char>{out}, fmt, args);
|
||||||
|
return out.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//creates a runtime argument from a c string
|
||||||
|
template<class T, class Char>
|
||||||
|
constexpr auto arg(const Char* name, T&& t){
|
||||||
|
return fmt::detail::runtime_arg<T,Char>{std::forward<T>(t), name};
|
||||||
|
}
|
||||||
|
|
||||||
|
//creates a runtime argument from a string view
|
||||||
|
template<class T, class Char>
|
||||||
|
constexpr auto arg(rexy::basic_string_view<Char> name, T&& t){
|
||||||
|
return fmt::detail::runtime_arg{std::forward<T>(t), name};
|
||||||
|
}
|
||||||
|
|
||||||
|
//create a compile time argument from a string literal
|
||||||
|
template<rexy::cx::string Name, class T>
|
||||||
|
constexpr auto arg(T&& t){
|
||||||
|
return fmt::detail::static_arg<T,Name>{t};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline namespace fmt_literals{
|
||||||
|
//create a compile time argument from a string literal
|
||||||
|
template<rexy::cx::string Name>
|
||||||
|
constexpr auto operator""_a(void){
|
||||||
|
return fmt::detail::arg_literal_result<Name>{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef REXY_REAL_STRINGIFY
|
||||||
|
#undef REXY_STRINGIFY
|
||||||
|
#undef REXY_THROW_FORMAT_ERROR
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@ -11,6 +11,9 @@ if(ENABLE_PROFILING)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_executable(basic_string "basic_string.cpp")
|
add_executable(basic_string "basic_string.cpp")
|
||||||
|
add_executable(format "format.cpp")
|
||||||
set_target_properties(basic_string PROPERTIES OUTPUT_NAME basic_string)
|
set_target_properties(basic_string PROPERTIES OUTPUT_NAME basic_string)
|
||||||
|
set_target_properties(format PROPERTIES OUTPUT_NAME format)
|
||||||
|
|
||||||
add_test(NAME basic_string-test COMMAND basic_string)
|
add_test(NAME basic_string-test COMMAND basic_string)
|
||||||
|
add_test(NAME format-test COMMAND format)
|
||||||
|
|||||||
111
tests/format.cpp
Normal file
111
tests/format.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#define LIBREXY_ENABLE_DEBUG_LEVEL 2
|
||||||
|
|
||||||
|
#include "rexy/format.hpp"
|
||||||
|
|
||||||
|
#include "rexy/string.hpp"
|
||||||
|
#include "rexy/debug_print.hpp"
|
||||||
|
#include "rexy/cx/string.hpp"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
using namespace rexy::str_literals;
|
||||||
|
|
||||||
|
[[noreturn]]
|
||||||
|
void test_fail(rexy::string_view failed, rexy::string_view expected){
|
||||||
|
rexy::debug::print_error("expected \"%s\", got \"%s\"\n", expected.c_str(), failed.c_str());
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
[[noreturn]]
|
||||||
|
void test_fail(void){
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PERFORM_TEST(desired, formt, ...) \
|
||||||
|
do{ \
|
||||||
|
rexy::string str = rexy::format((formt) __VA_OPT__(,) __VA_ARGS__); \
|
||||||
|
if(str != (desired)){ \
|
||||||
|
test_fail(str, desired); \
|
||||||
|
} \
|
||||||
|
}while(0)
|
||||||
|
|
||||||
|
struct X
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
constexpr operator int(void)const{return i;}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Char>
|
||||||
|
class rexy::formatter<X,Char> : public rexy::formatter<int,Char>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<class FormatContext>
|
||||||
|
auto format(X&, FormatContext& ctx) -> decltype(ctx.out()){
|
||||||
|
*(ctx.out()++) = 'X';
|
||||||
|
return std::move(ctx.out());
|
||||||
|
//return rexy::formatter<int,Char>::format(t.i, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void do_brace_escapes(void){
|
||||||
|
PERFORM_TEST("this is } a test string"_sv, "this is }} a test string", nullptr);
|
||||||
|
PERFORM_TEST("this is a test {string"_sv, "this is a test {{string", nullptr);
|
||||||
|
PERFORM_TEST("this }is a test {string"_sv, "this }}is a test {{string", nullptr);
|
||||||
|
PERFORM_TEST("this {is a tes}t string"_sv, "this {{is a tes}}t string", nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void do_empty_format(void){
|
||||||
|
do{
|
||||||
|
rexy::string str = rexy::format("{}", 0);
|
||||||
|
if(str != "0"_sv){
|
||||||
|
test_fail(str.c_str(), "0"_sv);
|
||||||
|
}
|
||||||
|
}while(0);
|
||||||
|
PERFORM_TEST("0"_sv, "{}", 0);
|
||||||
|
PERFORM_TEST("0"_sv, "{}", 0ul);
|
||||||
|
PERFORM_TEST("0"_sv, "{}", short{0});
|
||||||
|
PERFORM_TEST("0"_sv, "{}", 0.0f);
|
||||||
|
PERFORM_TEST("0x0"_sv, "{}", nullptr);
|
||||||
|
PERFORM_TEST("true"_sv, "{}", true);
|
||||||
|
PERFORM_TEST("cstring"_sv, "{}", "cstring");
|
||||||
|
PERFORM_TEST("string_view"_sv, "{}", "string_view"_sv);
|
||||||
|
PERFORM_TEST("string"_sv, "{}", rexy::string{"string"});
|
||||||
|
PERFORM_TEST("c"_sv, "{}", 'c');
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_index_specifiers(void){
|
||||||
|
PERFORM_TEST("2"_sv, "{2}", 0, 1, 2);
|
||||||
|
PERFORM_TEST("0"_sv, "{0}", 0, 1, 2);
|
||||||
|
PERFORM_TEST("1"_sv, "{1}", "string", 1, 2.8f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_dynamic_index_specifiers(void){
|
||||||
|
PERFORM_TEST("| 5|"_sv, "|{:{}}|", 5, 10);
|
||||||
|
PERFORM_TEST("| 5.80|"_sv, "|{:{}.{}f}|", 5.8f, 10, 2);
|
||||||
|
PERFORM_TEST("| 5|"_sv, "|{1:{0}}|", 10, 5);
|
||||||
|
PERFORM_TEST("| 5.80|"_sv, "|{0:{4}.{1}f}|", 5.8f, 2, 7, 8, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_fill_and_align(void){
|
||||||
|
PERFORM_TEST("*****6"_sv, "{:*>6}", 6);
|
||||||
|
PERFORM_TEST("6*****"_sv, "{:*<6}", 6);
|
||||||
|
PERFORM_TEST("**6***"_sv, "{:*^6}", 6);
|
||||||
|
|
||||||
|
PERFORM_TEST("*8.20*"_sv, "{:*^6.2f}", 8.2f);
|
||||||
|
PERFORM_TEST("-2****"_sv, "{:*<6}", -2);
|
||||||
|
PERFORM_TEST("***str"_sv, "{:*>6}", "str");
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO more test coverage
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
do_brace_escapes();
|
||||||
|
do_empty_format();
|
||||||
|
do_index_specifiers();
|
||||||
|
do_dynamic_index_specifiers();
|
||||||
|
do_fill_and_align();
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user