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()
|
||||
|
||||
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(format PROPERTIES OUTPUT_NAME format)
|
||||
|
||||
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