0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-06-01 01:28:54 +02:00

ircd::fmt: Internalize specifier related; various cleanup.

This commit is contained in:
Jason Volk 2018-05-30 09:03:16 -07:00
parent b5104d6504
commit 7825114281
2 changed files with 79 additions and 80 deletions

View file

@ -19,8 +19,6 @@ namespace ircd::fmt
IRCD_EXCEPTION(error, invalid_type);
IRCD_EXCEPTION(error, illegal);
struct spec;
struct specifier;
struct sprintf;
struct vsprintf;
struct snprintf;
@ -28,53 +26,9 @@ namespace ircd::fmt
struct snstringf;
struct vsnstringf;
template<size_t MAX> struct bsprintf;
//
// Module API
//
constexpr char SPECIFIER
{
'%'
};
constexpr char SPECIFIER_TERMINATOR
{
'$'
};
using arg = std::tuple<const void *, std::type_index>;
const std::map<string_view, specifier *, std::less<>> &specifiers();
}
// Structural representation of a format specifier
struct ircd::fmt::spec
{
char sign {'+'};
ushort width {0};
string_view name;
spec() = default;
};
// A format specifier handler module.
// This allows a new "%foo" to be defined with custom handling.
class ircd::fmt::specifier
{
std::set<std::string> names;
public:
virtual bool operator()(char *&out, const size_t &max, const spec &, const arg &) const = 0;
specifier(const std::initializer_list<std::string> &names);
specifier(const std::string &name);
virtual ~specifier() noexcept;
};
//
// User API
//
/// Typesafe snprintf() from formal grammar and RTTI.
///
/// This function accepts a format string and a variable number of arguments
@ -85,10 +39,6 @@ class ircd::fmt::specifier
/// pass an std::string directly without calling c_str(), as well as pass a
/// non-null-terminated string_view safely.
///
/// Furthermore, other features of ircd::fmt enable custom format specifiers
/// and handling of types not recognized by existing grammars through this
/// function.
///
class ircd::fmt::snprintf
{
const char *fstart; // Current running position in the fmtstr
@ -183,12 +133,13 @@ struct ircd::fmt::vsnstringf
const va_rtti &ap)
:std::string
{
[&max, &fmt, &ap]
ircd::string(max, [&fmt, &ap]
(const mutable_buffer &buf) -> string_view
{
std::string ret(max, char{});
ret.resize(vsnprintf(const_cast<char *>(ret.data()), ret.size() + 1, fmt, ap));
return ret;
}()
using buffer::data;
using buffer::size;
return vsnprintf(data(buf), size(buf), fmt, ap);
})
}{}
};

View file

@ -10,33 +10,84 @@
#include <ircd/spirit.h>
namespace ircd::fmt
{
using namespace ircd::spirit;
struct spec;
struct specifier;
constexpr char SPECIFIER
{
'%'
};
constexpr char SPECIFIER_TERMINATOR
{
'$'
};
struct parser extern const parser;
extern std::map<string_view, specifier *, std::less<>> specifiers;
bool is_specifier(const string_view &name);
void handle_specifier(char *&out, const size_t &max, const uint &idx, const spec &, const arg &);
template<class generator> bool generate_string(char *&out, const generator &gen, const arg &val);
template<class T, class lambda> bool visit_type(const arg &val, lambda&& closure);
}
// Structural representation of a format specifier
struct ircd::fmt::spec
{
char sign {'+'};
ushort width {0};
string_view name;
spec() = default;
};
BOOST_FUSION_ADAPT_STRUCT
(
ircd::fmt::spec,
( decltype(ircd::fmt::spec::sign), sign )
( decltype(ircd::fmt::spec::width), width )
( decltype(ircd::fmt::spec::name), name )
( decltype(ircd::fmt::spec::sign), sign )
( decltype(ircd::fmt::spec::width), width )
( decltype(ircd::fmt::spec::name), name )
)
namespace ircd {
namespace fmt {
// A format specifier handler module.
// This allows a new "%foo" to be defined with custom handling.
class ircd::fmt::specifier
{
std::set<std::string> names;
using namespace ircd::spirit;
public:
virtual bool operator()(char *&out, const size_t &max, const spec &, const arg &) const = 0;
std::map<string_view, specifier *, std::less<>> _specifiers;
specifier(const std::initializer_list<std::string> &names);
specifier(const std::string &name);
virtual ~specifier() noexcept;
};
bool is_specifier(const string_view &name);
void handle_specifier(char *&out, const size_t &max, const uint &idx, const spec &, const arg &);
template<class generator> bool generate_string(char *&out, const generator &gen, const arg &val);
template<class T, class lambda> bool visit_type(const arg &val, lambda&& closure);
decltype(ircd::fmt::specifiers)
ircd::fmt::specifiers;
struct parser
struct ircd::fmt::parser
:qi::grammar<const char *, fmt::spec>
{
template<class R = unused_type> using rule = qi::rule<const char *, R>;
const rule<> specsym { lit(SPECIFIER) ,"format specifier" };
const rule<> specterm { lit(SPECIFIER_TERMINATOR) ,"specifier termination" };
const rule<> specsym
{
lit(SPECIFIER)
,"format specifier"
};
const rule<> specterm
{
lit(SPECIFIER_TERMINATOR)
,"specifier termination"
};
const rule<string_view> name
{
raw[repeat(1,14)[char_("A-Za-z")]]
@ -57,7 +108,10 @@ struct parser
spec %= specsym >> -(char_('+') | char_('-')) >> -ushort_ >> name[is_valid] >> -specterm;
}
}
const parser;
const ircd::fmt::parser;
namespace ircd {
namespace fmt {
struct string_specifier
:specifier
@ -340,12 +394,6 @@ const
return !fstart || fstop >= fend || remaining() == 0;
}
const decltype(fmt::_specifiers) &
fmt::specifiers()
{
return _specifiers;
}
fmt::specifier::specifier(const std::string &name)
:specifier{{name}}
{
@ -362,20 +410,20 @@ fmt::specifier::specifier(const std::initializer_list<std::string> &names)
};
for(const auto &name : this->names)
_specifiers.emplace(name, this);
specifiers.emplace(name, this);
}
fmt::specifier::~specifier()
noexcept
{
for(const auto &name : names)
_specifiers.erase(name);
specifiers.erase(name);
}
bool
fmt::is_specifier(const string_view &name)
{
return specifiers().count(name);
return specifiers.count(name);
}
void
@ -387,7 +435,7 @@ fmt::handle_specifier(char *&out,
try
{
const auto &type(get<1>(val));
const auto &handler(*specifiers().at(spec.name));
const auto &handler(*specifiers.at(spec.name));
if(unlikely(!handler(out, max, spec, val)))
throw invalid_type
{