diff --git a/include/rexy/detail/format/arg_store.hpp b/include/rexy/detail/format/arg_store.hpp
new file mode 100644
index 0000000..f6ac780
--- /dev/null
+++ b/include/rexy/detail/format/arg_store.hpp
@@ -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 .
+*/
+
+#ifndef REXY_DETAIL_FORMAT_ARG_STORE_HPP
+#define REXY_DETAIL_FORMAT_ARG_STORE_HPP
+
+#include //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 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;
+
+ struct format_data
+ {
+ friend class basic_format_arg_store;
+ private:
+ unsigned char m_data[
+ (sizeof(arg_info) * num_args) +
+ (sizeof(basic_string_view) * num_named_args) +
+ (sizeof(stored_type_t) + ...)
+ ] = {};
+
+ arg_info* info(void){
+ return reinterpret_cast(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(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
+ void store_arg(std::size_t argnum, Arg&& arg);
+ template
+ void store_arg(std::size_t argnum, Arg&& arg);
+
+ template
+ void store_args(SArgs&&... args);
+ };
+
+ //Specialized for empty parameter pack
+ template
+ class basic_format_arg_store
+ {
+ public:
+ static constexpr std::size_t num_args = 0;
+ };
+
+
+
+
+}
+
+#endif
diff --git a/include/rexy/detail/format/arg_store.tpp b/include/rexy/detail/format/arg_store.tpp
new file mode 100644
index 0000000..dc43262
--- /dev/null
+++ b/include/rexy/detail/format/arg_store.tpp
@@ -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 .
+*/
+
+#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 //forward
+#include //addressof
+#include //size_t
+
+namespace rexy::fmt::detail{
+
+ ///////////////////////basic_format_arg_store////////////////////////
+ template
+ basic_format_arg_store::basic_format_arg_store(Args&&... args){
+ store_args(std::forward(args)...);
+ }
+ template
+ template
+ void basic_format_arg_store::store_arg(std::size_t argnum, Arg&& arg){
+ using stored_type = stored_type_t;
+ arg_info& ai = packed_data.info()[argnum];
+ ai.type = map_to_storage_enum_v;
+
+ //convert to one of the storable types
+ stored_type st{std::forward(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
+ template
+ void basic_format_arg_store::store_arg(std::size_t argnum, Arg&& arg){
+ using stored_type = stored_type_t;
+ arg_info& ai = packed_data.info()[argnum];
+ ai.type = map_to_storage_enum_v;
+ ai.named = true;
+ const std::size_t name_size = sizeof(format_string_view);
+
+ //convert to one of the storable types
+ stored_type st{std::forward(arg).value};
+ format_string_view 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
+ template
+ void basic_format_arg_store::store_args(SArgs&&... args){
+ std::size_t argnum = 0;
+ (store_arg(argnum++, std::forward(args)), ...);
+ }
+
+}
+
+#endif
diff --git a/include/rexy/detail/format/basic_types.hpp b/include/rexy/detail/format/basic_types.hpp
new file mode 100644
index 0000000..b6fe713
--- /dev/null
+++ b/include/rexy/detail/format/basic_types.hpp
@@ -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 .
+*/
+
+#ifndef REXY_DETAIL_FORMAT_BASIC_TYPES_HPP
+#define REXY_DETAIL_FORMAT_BASIC_TYPES_HPP
+
+#include //size_t
+
+#include "storage.hpp"
+
+namespace rexy::fmt::detail{
+
+ static constexpr std::size_t invalid_arg_index = std::size_t{-1};
+
+
+ template
+ 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
+ struct dynamic_format_specs : public format_specs{
+ union dyn_val{
+ std::size_t index = invalid_arg_index;
+ format_string_view name;
+ }dyn_width, dyn_precision;
+
+ dyn_type width_type = dyn_type::index;
+ dyn_type precision_type = dyn_type::index;
+ };
+ template
+ constexpr void normalize_dynamic_format_specs(FormatCtx& ctx, dynamic_format_specs& specs);
+
+}
+
+namespace rexy{
+
+ template
+ struct format_to_n_result{
+ OutIt out;
+ std::size_t size;
+ };
+
+}
+
+#endif
diff --git a/include/rexy/detail/format/basic_types.tpp b/include/rexy/detail/format/basic_types.tpp
new file mode 100644
index 0000000..961330b
--- /dev/null
+++ b/include/rexy/detail/format/basic_types.tpp
@@ -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 .
+*/
+
+#ifndef REXY_DETAIL_FORMAT_BASIC_TYPES_TPP
+#define REXY_DETAIL_FORMAT_BASIC_TYPES_TPP
+
+#include "basic_types.hpp"
+
+#include "format_args.hpp"
+
+#include //size_t
+
+namespace rexy::fmt::detail{
+
+ template
+ constexpr void normalize_dynamic_format_specs(FormatCtx& ctx, dynamic_format_specs& 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
diff --git a/include/rexy/detail/format/context_handler.hpp b/include/rexy/detail/format/context_handler.hpp
new file mode 100644
index 0000000..2069b00
--- /dev/null
+++ b/include/rexy/detail/format/context_handler.hpp
@@ -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 .
+*/
+
+#ifndef REXY_DETAIL_FORMAT_CONTEXT_HANDLER_HPP
+#define REXY_DETAIL_FORMAT_CONTEXT_HANDLER_HPP
+
+#include //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
+
+namespace rexy::fmt::detail{
+
+ //Holds the format and parse contexts and performs parse+format calls for replacement fields
+ template
+ struct format_handler
+ {
+ public:
+ using char_type = Char;
+ using fmt_ctx_t = fmt_context_t;
+ using parse_ctx_t = parse_context_t;
+ using out_it_t = output_iterator_t;
+ using fmt_it_t = typename parse_ctx_t::iterator;
+ using handle_t = typename basic_format_arg::handle;
+ template
+ using fmter_t = typename fmt_ctx_t::template formatter_type;
+
+ public:
+ fmt_ctx_t fmt_ctx;
+ parse_ctx_t parse_ctx;
+
+ public:
+ format_handler(out_it_t output, basic_string_view fmt, basic_format_args args);
+ format_handler(out_it_t output, basic_string_view fmt, basic_format_args 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
+ format_handler(output_iterator_t, basic_string_view, basic_format_args>) -> format_handler;
+
+ //Constant-evaluated version of format_handler. Does not perform formatting but will complete a parsing
+ //pass during compile
+ template
+ class format_checker
+ {
+ public:
+ using char_type = Char;
+ using parse_ctx_t = parse_context_t;
+ 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 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
+ constexpr fmt_it_t do_format_spec_impl(fmt_it_t start, fmt_it_t last, std::size_t id);
+ };
+
+}
+
+#endif
diff --git a/include/rexy/detail/format/context_handler.tpp b/include/rexy/detail/format/context_handler.tpp
new file mode 100644
index 0000000..52dcdd0
--- /dev/null
+++ b/include/rexy/detail/format/context_handler.tpp
@@ -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 .
+*/
+
+#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
+#include //size_t
+#include //remove_cvref
+
+namespace rexy::fmt::detail{
+
+ ///////////////////////////////format_handler//////////////////////////////////
+ template
+ format_handler::format_handler(out_it_t output, basic_string_view fmt, basic_format_args args):
+ fmt_ctx(args, output),
+ parse_ctx(fmt){}
+ template
+ format_handler::format_handler(out_it_t output, basic_string_view fmt, basic_format_args args, const std::locale& l):
+ fmt_ctx(args, output, l),
+ parse_ctx(fmt){}
+
+ template
+ constexpr auto format_handler::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
+ constexpr auto format_handler::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, parse_ctx, specs}, arg);
+ return parse_ctx.begin();
+ }
+
+ template
+ constexpr auto format_handler::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, parse_ctx}, arg);
+ return parse_ctx.begin();
+ }
+
+ template
+ constexpr std::size_t format_handler::on_arg_id(void){
+ return parse_ctx.next_arg_id();
+ }
+ template
+ constexpr std::size_t format_handler::on_arg_id(std::size_t i){
+ parse_ctx.check_arg_id(i);
+ return i;
+ }
+ template
+ constexpr std::size_t format_handler::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
+ consteval format_checker::format_checker(basic_string_view fmt, std::size_t numargs):
+ parse_ctx(fmt, numargs){}
+ template
+ constexpr auto format_checker::do_raw(fmt_it_t, fmt_it_t last) -> fmt_it_t{
+ return last;
+ }
+ template
+ constexpr auto format_checker::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
+ constexpr void format_checker::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
+ template
+ constexpr auto format_checker::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;
+ using base_type = std::remove_cvref_t;
+ using stored_type = stored_type_t;
+ if constexpr(Handle){
+ using fmter_t = typename fmt_ctx_t::template formatter_type;
+ fmter_t fmter{};
+ return fmter.parse(parse_ctx);
+ }else{
+ dynamic_format_specs specs{};
+ format_specs_checker> handler{
+ cx_format_specs_handler{parse_ctx, specs},
+ detail::map_to_storage_enum_v
+ };
+ return parse::perform_standard_parse(start, last, handler);
+ }
+ }else{
+ if constexpr(sizeof...(FArgs) > 0){
+ return do_format_spec_impl(start, last, id);
+ }
+ }
+ REXY_THROW_FORMAT_ERROR("Missing argument");
+ }
+
+ template
+ constexpr std::size_t format_checker::on_arg_id(void){
+ return parse_ctx.next_arg_id();
+ }
+ template
+ constexpr std::size_t format_checker::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
+ constexpr std::size_t format_checker::on_arg_id(fmt_it_t start, fmt_it_t last){
+ const std::size_t id = find_static_named_arg_id(start, last);
+ if(id == invalid_arg_index){
+ REXY_THROW_FORMAT_ERROR("No such named arg");
+ }
+ return on_arg_id(id);
+ }
+
+
+}
+
+#endif
diff --git a/include/rexy/detail/format/format_args.hpp b/include/rexy/detail/format/format_args.hpp
new file mode 100644
index 0000000..f30e4af
--- /dev/null
+++ b/include/rexy/detail/format/format_args.hpp
@@ -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 .
+*/
+
+#ifndef REXY_DETAIL_FORMAT_FORMAT_ARGS_HPP
+#define REXY_DETAIL_FORMAT_FORMAT_ARGS_HPP
+
+#include //size_t
+#include //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 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&, Context&)const;
+ public:
+ const void* m_data;
+ format_ptr_t m_format_impl;
+ public:
+ template
+ explicit handle(T&& t);
+ template
+ void format_impl(basic_format_parse_context& parse_ctx, Context& format_ctx)const;
+ void format(basic_format_parse_context& 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 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 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 basic_format_args
+ {
+ public:
+ using char_type = detail::extract_char_type_from_context_t;
+ 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& store)noexcept;
+ template
+ basic_format_args(const detail::basic_format_arg_store& store)noexcept;
+
+ basic_format_arg get(std::size_t i)const noexcept;
+ basic_format_arg 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
+ decltype(auto) visit_format_arg(Fun&& fun, const basic_format_arg& arg);
+}
+
+#endif
diff --git a/include/rexy/detail/format/format_args.tpp b/include/rexy/detail/format/format_args.tpp
new file mode 100644
index 0000000..9e99d9b
--- /dev/null
+++ b/include/rexy/detail/format/format_args.tpp
@@ -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 .
+*/
+
+#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 //forward
+#include //remove_cvref, add_lvalue_reference, add_pointer, add_const, conditional
+#include //size_t
+#include //addressof
+
+namespace rexy::fmt{
+
+ /////////////////////////////basic_format_arg::handle//////////////////////////////
+ template
+ template
+ basic_format_arg::handle::handle(T&& t):
+ m_data(std::addressof(t)),
+ m_format_impl(&handle::format_impl>){}
+ template
+ template
+ void basic_format_arg::handle::format_impl(basic_format_parse_context& parse_ctx, Context& format_ctx)const{
+ using value_type = std::remove_cvref_t;
+ using formatter_t = typename Context::template formatter_type;
+ using qualified_type = std::conditional_t,
+ std::add_const_t,
+ value_type>;
+ using reference_type = std::add_lvalue_reference_t;
+ using pointer_type = std::add_pointer_t;
+ using const_pointer_type = std::add_pointer_t>;
+
+ reference_type ref = *const_cast(reinterpret_cast(m_data));
+
+ formatter_t fmt;
+ parse_ctx.advance_to(fmt.parse(parse_ctx));
+ format_ctx.advance_to(fmt.format(ref, format_ctx));
+ }
+
+ template
+ void basic_format_arg::handle::format(basic_format_parse_context& parse_ctx, Context& format_ctx)const{
+ //Pretty epic gamer moment here
+ (this->*m_format_impl)(parse_ctx, format_ctx);
+ }
+
+ ///////////////////////////////basic_format_arg////////////////////////////////
+ template
+ basic_format_arg::basic_format_arg(bool b)noexcept:
+ bool_v(b),
+ active_member(detail::storage_type::bool_t){}
+ template
+ basic_format_arg::basic_format_arg(char_type b)noexcept:
+ char_v(b),
+ active_member(detail::storage_type::char_t){}
+ template
+ basic_format_arg::basic_format_arg(int b)noexcept:
+ int_v(b),
+ active_member(detail::storage_type::int_t){}
+ template
+ basic_format_arg::basic_format_arg(unsigned int b)noexcept:
+ uint_v(b),
+ active_member(detail::storage_type::uint_t){}
+ template
+ basic_format_arg::basic_format_arg(long long b)noexcept:
+ long_long_v(b),
+ active_member(detail::storage_type::long_long_t){}
+ template
+ basic_format_arg::basic_format_arg(unsigned long long b)noexcept:
+ ulong_long_v(b),
+ active_member(detail::storage_type::ulong_long_t){}
+ template
+ basic_format_arg::basic_format_arg(float b)noexcept:
+ float_v(b),
+ active_member(detail::storage_type::float_t){}
+ template
+ basic_format_arg::basic_format_arg(double b)noexcept:
+ double_v(b),
+ active_member(detail::storage_type::double_t){}
+ template
+ basic_format_arg::basic_format_arg(long double b)noexcept:
+ long_double_v(b),
+ active_member(detail::storage_type::long_double_t){}
+ template
+ basic_format_arg::basic_format_arg(const char_type* b)noexcept:
+ char_ptr_v(b),
+ active_member(detail::storage_type::char_ptr_t){}
+ template
+ basic_format_arg::basic_format_arg(basic_string_view b)noexcept:
+ string_v(b),
+ active_member(detail::storage_type::string_t){}
+ template
+ basic_format_arg::basic_format_arg(const void* b)noexcept:
+ ptr_v(b),
+ active_member(detail::storage_type::ptr_t){}
+ template
+ basic_format_arg::basic_format_arg(const handle b)noexcept:
+ custom_v(b),
+ active_member(detail::storage_type::custom_t){}
+ template
+ detail::storage_type basic_format_arg::type(void)const{
+ return active_member;
+ }
+ template
+ basic_format_arg::operator bool(void)const noexcept{
+ return active_member != detail::storage_type::none_t;
+ }
+
+ ///////////////////////////////basic_format_args////////////////////////////////
+ template
+ constexpr basic_format_args::basic_format_args(const detail::basic_format_arg_store&)noexcept{}
+ template
+ template
+ basic_format_args::basic_format_args(const detail::basic_format_arg_store& store)noexcept:
+ m_info(store.packed_data.info()),
+ m_data(store.packed_data.data()),
+ m_num_args(sizeof...(Args)){}
+
+ template
+ basic_format_arg basic_format_args::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) : 0);
+ const unsigned char* const data = m_data + offset;
+
+ switch(m_info[i].type){
+ case detail::storage_type::bool_t:
+ return basic_format_arg{*reinterpret_cast(data)};
+ case detail::storage_type::char_t:
+ return basic_format_arg{*reinterpret_cast(data)};
+ case detail::storage_type::int_t:
+ return basic_format_arg{*reinterpret_cast(data)};
+ case detail::storage_type::uint_t:
+ return basic_format_arg{*reinterpret_cast(data)};
+ case detail::storage_type::long_long_t:
+ return basic_format_arg{*reinterpret_cast(data)};
+ case detail::storage_type::ulong_long_t:
+ return basic_format_arg{*reinterpret_cast(data)};
+ case detail::storage_type::float_t:
+ return basic_format_arg{*reinterpret_cast(data)};
+ case detail::storage_type::double_t:
+ return basic_format_arg{*reinterpret_cast(data)};
+ case detail::storage_type::long_double_t:
+ return basic_format_arg{*reinterpret_cast(data)};
+ case detail::storage_type::char_ptr_t:
+ return basic_format_arg{*reinterpret_cast(data)};
+ case detail::storage_type::string_t:
+ return basic_format_arg{*reinterpret_cast*>(data)};
+ case detail::storage_type::ptr_t:
+ return basic_format_arg{reinterpret_cast(*reinterpret_cast(data))};
+ case detail::storage_type::custom_t:
+ return basic_format_arg{*reinterpret_cast::handle*>(data)};
+ default:
+ return basic_format_arg{};
+ };
+ }
+ template
+ basic_format_arg basic_format_args::get(const char_type* first, const char_type* last)const{
+ return get(get_index(first, last));
+ }
+ template
+ std::size_t basic_format_args::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>(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
+ decltype(auto) visit_format_arg(Fun&& fun, const basic_format_arg& arg){
+ switch(arg.type()){
+ case detail::storage_type::bool_t:
+ return std::forward(fun)(arg.bool_v);
+ case detail::storage_type::char_t:
+ return std::forward(fun)(arg.char_v);
+ case detail::storage_type::int_t:
+ return std::forward(fun)(arg.int_v);
+ case detail::storage_type::uint_t:
+ return std::forward(fun)(arg.uint_v);
+ case detail::storage_type::long_long_t:
+ return std::forward(fun)(arg.long_long_v);
+ case detail::storage_type::ulong_long_t:
+ return std::forward(fun)(arg.ulong_long_v);
+ case detail::storage_type::float_t:
+ return std::forward(fun)(arg.float_v);
+ case detail::storage_type::double_t:
+ return std::forward(fun)(arg.double_v);
+ case detail::storage_type::long_double_t:
+ return std::forward(fun)(arg.long_double_v);
+ case detail::storage_type::char_ptr_t:
+ return std::forward(fun)(arg.char_ptr_v);
+ case detail::storage_type::string_t:
+ return std::forward(fun)(arg.string_v);
+ case detail::storage_type::ptr_t:
+ return std::forward