Add rexy::format and rexy::print functions

This commit is contained in:
rexy712 2022-06-22 13:42:53 -07:00
parent 1bc234cbca
commit 3d17d3ec8c
35 changed files with 4776 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View 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
View 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

View File

@ -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
View 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();
}