Skip to content

Instantly share code, notes, and snippets.

@komiga
Last active March 4, 2024 21:49
Show Gist options
  • Select an option

  • Save komiga/5790115 to your computer and use it in GitHub Desktop.

Select an option

Save komiga/5790115 to your computer and use it in GitHub Desktop.
constexpr format string parsing. I am fully prepared to suffer the consequences.
#include <cstddef>
#include <stdexcept>
#include <utility>
#include <string>
#include <iomanip>
#include <sstream>
#include <iostream>
using String=std::string;
using StringStream=std::stringstream;
template<typename T>
constexpr T
min_ce(
T const x,
T const y
) noexcept {
return
x < y
? x
: y
;
}
enum : std::size_t {
ELEMENTS_MAX = 16u,
ELEMENT_LAST = ELEMENTS_MAX - 1,
ELEMENT_FLAG_COUNT = 4u
};
enum : char {
ELEMENT_CHAR = '%'
};
enum class ElementType : unsigned {
end = 0u,
esc,
dec,
hex,
oct,
flt,
boo,
ptr,
str
};
enum class ElementFlags : unsigned {
show_base = 1 << 0,
show_sign = 1 << 1,
zero_padded = 1 << 2,
left_align = 1 << 3,
none = 0x0000,
all
=show_base
|show_sign
|zero_padded
|left_align
,
permitted_end = none,
permitted_esc = none,
permitted_dec = all & ~show_base,
permitted_hex = all & ~show_sign,
permitted_oct = all & ~show_sign,
permitted_flt = all & ~show_base,
permitted_boo = left_align,
permitted_ptr = all,
permitted_str = left_align
};
template<
typename T,
class = void
>
struct type_to_element {
using cast=T;
static constexpr bool valid = false;
static constexpr bool
type_matches(
ElementType const type
) noexcept {
return false;
}
};
// integral
template<
typename T
>
struct type_to_element<
T,
typename std::enable_if<
!std::is_same<
bool,
typename std::remove_const<
typename std::remove_reference<T>::type
>::type
>::value &&
std::is_integral<
typename std::remove_reference<T>::type
>::value
>::type
> {
using cast=T;
static constexpr bool valid = true;
static constexpr bool
type_matches(
ElementType const type
) noexcept {
return false
|| ElementType::dec == type
|| ElementType::hex == type
|| ElementType::oct == type
? true
: false;
}
};
// floating-point
template<
typename T
>
struct type_to_element<
T,
typename std::enable_if<
std::is_floating_point<
typename std::remove_reference<T>::type
>::value
>::type
> {
using cast=T;
static constexpr bool valid = true;
static constexpr bool
type_matches(
ElementType const type
) noexcept {
return ElementType::flt == type;
}
};
// boolean
template<
typename T
>
struct type_to_element<
T,
typename std::enable_if<
std::is_same<
bool,
typename std::remove_const<
typename std::remove_reference<T>::type
>::type
>::value
>::type
> {
using cast=T;
static constexpr bool valid = true;
static constexpr bool
type_matches(
ElementType const type
) noexcept {
return ElementType::boo == type;
}
};
// pointer
template<
typename T
>
struct type_to_element<
T,
typename std::enable_if<
std::is_pointer<
typename std::remove_reference<T>::type
>::value ||
std::is_same< // really?
decltype(nullptr),
typename std::remove_reference<T>::type
>::value
>::type
> {
using cast=void*;
static constexpr bool valid = true;
static constexpr bool
type_matches(
ElementType const type
) noexcept {
return ElementType::ptr == type;
}
};
// string/object
template<
typename T
>
struct type_to_element<
T,
typename std::enable_if<
// le hack
!std::is_integral<
typename std::remove_reference<T>::type
>::value &&
!std::is_floating_point<
typename std::remove_reference<T>::type
>::value && (
(
std::is_array<typename std::remove_reference<T>::type>::value &&
1u == std::rank<typename std::remove_reference<T>::type>::value &&
std::is_same<
char,
typename std::remove_extent<
typename std::remove_reference<T>::type
>::type
>::value
) ||
std::is_same<
char const*,
typename std::remove_const<
typename std::remove_reference<T>::type
>::type
>::value ||
std::is_same<
std::ostream&,
decltype(std::declval<std::ostream&>() << std::declval<T&>())
>::value
)>::type
> {
using cast=T;
static constexpr bool valid = true;
static constexpr bool
type_matches(
ElementType const type
) noexcept {
return ElementType::str == type;
}
};
namespace {
static constexpr unsigned const
s_flags_permitted[]{
static_cast<unsigned>(ElementFlags::permitted_end),
static_cast<unsigned>(ElementFlags::permitted_esc),
static_cast<unsigned>(ElementFlags::permitted_dec),
static_cast<unsigned>(ElementFlags::permitted_hex),
static_cast<unsigned>(ElementFlags::permitted_oct),
static_cast<unsigned>(ElementFlags::permitted_flt),
static_cast<unsigned>(ElementFlags::permitted_boo),
static_cast<unsigned>(ElementFlags::permitted_ptr),
static_cast<unsigned>(ElementFlags::permitted_str)
};
static constexpr char const
s_type_values[]{
'\0',
ELEMENT_CHAR,
'd',
'x',
'o',
'f',
'b',
'p',
's'
},
s_type_name_invalid[] = "INVALID",
* const s_type_names[]{
"end",
"esc",
"dec",
"hex",
"oct",
"flt",
"boo",
"ptr",
"str"
};
} // anonymous namespace
constexpr char const* const
get_element_type_name(
ElementType const type
) noexcept {
return
std::extent<decltype(s_type_names)>::value > static_cast<unsigned>(type)
? s_type_names[static_cast<unsigned>(type)]
: s_type_name_invalid
;
}
constexpr std::size_t
element_flag_count(
unsigned const flags,
std::size_t const count = 0u,
std::size_t const index = 0u
) {
return
index < ELEMENT_FLAG_COUNT
? element_flag_count(
flags,
count + (0 != (flags & (1 << index))),
index + 1u
)
: count
;
}
enum class ParticleKind : unsigned {
invalid = 0u,
type,
flag
};
struct Particle {
ParticleKind const kind;
char const value;
union {
ElementType const type;
unsigned const flag;
};
enum invalid_t {
invalid
};
constexpr
Particle(
enum invalid_t const
) noexcept
: kind(ParticleKind::invalid)
, value('\0')
, flag(0u)
{}
constexpr
Particle(
char const value,
ElementType const type
) noexcept
: kind(ParticleKind::type)
, value(value)
, type(type)
{}
constexpr
Particle(
char const value,
ElementFlags const flag
) noexcept
: kind(ParticleKind::flag)
, value(value)
, flag(static_cast<unsigned>(flag))
{}
};
constexpr Particle const
s_particles[]{
{ELEMENT_CHAR, ElementType::esc},
{'d', ElementType::dec},
{'x', ElementType::hex},
{'o', ElementType::oct},
{'f', ElementType::flt},
{'b', ElementType::boo},
{'p', ElementType::ptr},
{'s', ElementType::str},
{'#', ElementFlags::show_base},
{'+', ElementFlags::show_sign},
{'0', ElementFlags::zero_padded},
{'-', ElementFlags::left_align},
{Particle::invalid}
};
constexpr Particle const&
particle_classify(
char const value,
std::size_t const index = 0u
) noexcept {
return false
|| ParticleKind::invalid == s_particles[index].kind
|| value == s_particles[index].value
? s_particles[index]
: particle_classify(value, index + 1u)
;
}
struct Format;
struct Element;
struct Element {
public:
Format const& fmt;
std::size_t const idx;
std::size_t const beg;
ElementType const type;
unsigned const flags;
std::size_t const end;
std::size_t const width;
enum invalid_t {
invalid
};
private:
bool _valid;
constexpr std::size_t
cons_beg(
std::size_t const pos
) const noexcept;
constexpr unsigned
cons_flags_inner(
Particle const& particle,
std::size_t const next,
unsigned const c_flags
) const noexcept;
constexpr unsigned
cons_flags(
std::size_t const pos,
unsigned const c_flags
) const noexcept;
constexpr ElementType
cons_type_inner(
Particle const& particle,
std::size_t const next
) const noexcept;
constexpr ElementType
cons_type(
std::size_t const pos
) const noexcept;
constexpr std::size_t
cons_end() const noexcept;
constexpr bool
flag_check() const noexcept;
constexpr bool
valid_check() const noexcept;
public:
constexpr
Element(
Format const& fmt,
std::size_t const index,
enum invalid_t const
) noexcept;
constexpr
Element(
Format const& fmt,
std::size_t const index,
std::size_t const pos
) noexcept;
};
struct Format {
public:
char const* const string;
std::size_t const size;
Element const elements[ELEMENTS_MAX + 1u];
std::size_t const element_count;
std::size_t const serial_count;
private:
constexpr Element
e(
std::size_t const index
) const noexcept {
return
0u < index && ElementType::end == this->elements[index - 1].type
? Element{*this, index, Element::invalid}
: Element{
*this,
index,
(0u == index) ? 0u : elements[index - 1].end
}
;
}
constexpr std::size_t
cons_count(
std::size_t const index = 0u
) const noexcept {
return false ? 0u
: ELEMENTS_MAX + 1u == index
? throw std::logic_error("element array overrun")
: ElementType::end == this->elements[index].type
? index
: cons_count(index + 1u)
;
}
constexpr std::size_t
cons_count_serial(
std::size_t const count = 0u,
std::size_t const index = 0u
) const noexcept {
return false ? 0u
: ELEMENTS_MAX + 1u == index
? throw std::logic_error("element array overrun")
: ElementType::end == this->elements[index].type
? count
: cons_count_serial(
count
+ static_cast<std::size_t>(
ElementType::esc != this->elements[index].type
),
index + 1u
)
;
}
public:
template<
std::size_t N
>
constexpr explicit
Format(
char const (&string)[N]
) noexcept
: string(string)
, size(N)
, elements{
e(0u ),e(1u ),
e(2u ),e(3u ),
e(4u ),e(5u ),
e(6u ),e(7u ),
e(8u ),e(9u ),
e(10u),e(11u),
e(12u),e(13u),
e(14u),e(15u),
e(16u)
}
, element_count(cons_count())
, serial_count(cons_count_serial())
{}
constexpr Element const*
begin() const noexcept { return elements; }
constexpr Element const*
end() const noexcept { return elements+ELEMENTS_MAX; }
constexpr std::size_t
next_serial_index(
std::size_t const index
) const noexcept {
return
ElementType::esc == this->elements[index + 1u].type
? next_serial_index(index + 1u)
: index + 1u
;
}
};
// beg
constexpr std::size_t
Element::cons_beg(
std::size_t const pos
) const noexcept {
return false ? 0u
: pos >= this->fmt.size
? this->fmt.size
: ELEMENT_CHAR == this->fmt.string[pos]
? pos
: cons_beg(pos + 1u);
}
// type
constexpr ElementType
Element::cons_type_inner(
Particle const& particle,
std::size_t const next
) const noexcept {
return false ? ElementType::end
: ParticleKind::invalid == particle.kind
? throw std::logic_error("format string overflow; malformed element")
: ParticleKind::flag == particle.kind
? cons_type(next)
: particle.type // encountered ParticleKind::type
;
}
constexpr ElementType
Element::cons_type(
std::size_t const pos
) const noexcept {
return false ? ElementType::end
: this->beg == this->fmt.size
? ElementType::end // no ELEMENT_CHAR was found
: pos >= this->fmt.size
? throw std::logic_error("format string overflow; malformed element")
: cons_type_inner(
particle_classify(this->fmt.string[pos]),
pos + 1u
)
;
}
// flags
constexpr unsigned
Element::cons_flags_inner(
Particle const& particle,
std::size_t const next,
unsigned const c_flags
) const noexcept {
return false ? 0u
: ParticleKind::invalid == particle.kind
? throw std::logic_error("format string overflow; malformed element")
: ParticleKind::flag == particle.kind
? cons_flags(next, c_flags | particle.flag)
: c_flags // encountered ParticleKind::type
;
}
constexpr unsigned
Element::cons_flags(
std::size_t const pos,
unsigned const c_flags
) const noexcept {
return false ? 0u
: ElementType::end == this->type
? 0u
: pos >= this->fmt.size
? throw std::logic_error("format string overflow; malformed element")
: cons_flags_inner(
particle_classify(this->fmt.string[pos]),
pos + 1u,
c_flags
)
;
}
// end
constexpr std::size_t
Element::cons_end() const noexcept {
return
ElementType::end == this->type
? this->fmt.size
: min_ce(
this->fmt.size,
this->beg + element_flag_count(this->flags) + 2u
)
;
}
// _valid
constexpr bool
Element::flag_check() const noexcept {
return
0u == (~s_flags_permitted[static_cast<unsigned>(this->type)] & this->flags);
}
constexpr bool
Element::valid_check() const noexcept {
return false ? false
: ELEMENTS_MAX == this->idx && ElementType::end != this->type
? throw std::logic_error("not enough space for elements in format string")
: !flag_check()
? throw std::logic_error("format flag(s) not valid with type")
: true;
}
constexpr
Element::Element(
Format const& fmt,
std::size_t const index,
enum invalid_t const
) noexcept
: fmt(fmt)
, idx(index)
, beg(fmt.size)
, type(ElementType::end)
, flags(0u)
, end(fmt.size)
, width(0u)
, _valid(false)
{}
constexpr
Element::Element(
Format const& fmt,
std::size_t const index,
std::size_t const pos
) noexcept
: fmt(fmt)
, idx(index)
, beg(cons_beg(pos))
, type(cons_type(this->beg + 1u))
, flags(cons_flags(this->beg + 1u, 0u))
, end(cons_end())
, width(0u)
, _valid(valid_check())
{}
std::ostream&
operator<<(
std::ostream& stream,
Element const& e
) {
stream
<< '{'
<< "idx=" << e.idx << ", "
<< "beg=" << e.beg << ", "
<< "end=" << e.end << ", "
<< "type=" << get_element_type_name(e.type)
;
if (ElementType::end != e.type) {
stream << ", blob=\"";
stream.write(
e.fmt.string + e.beg,
static_cast<std::streamsize>(e.end - e.beg)
);
stream << "\"}";
} else {
stream << '}';
}
return stream;
}
std::ostream&
operator<<(
std::ostream& stream,
Format const& f
) {
stream
<< "Format {\n"
<< " string=\"" << f.string << "\",\n"
<< " size=" << f.size << ",\n"
<< " element_count=" << f.element_count << ",\n"
<< " serial_count=" << f.serial_count << ",\n"
<< " elements: {\n"
;
for (
std::size_t index = 0u;
ELEMENTS_MAX > index;
++index
) {
std::cout << " " << f.elements[index] << '\n';
}
stream << " }\n}";
return stream;
}
// format
namespace {
template<Format const&, typename...>
struct format_type_check_impl;
template<
Format const& format
>
struct format_type_check_impl<format> {
static constexpr bool
g(
std::size_t const
) noexcept {
return true;
}
};
template<
Format const& format,
typename I,
typename... P
>
struct format_type_check_impl<format, I, P...> {
static constexpr bool
g(
std::size_t const index
) noexcept {
return false
|| !type_to_element<I>::valid
|| !type_to_element<I>::type_matches(format.elements[index].type)
? throw std::logic_error("type of argument does not match element")
: format_type_check_impl<format, P...>::g(
format.next_serial_index(index)
);
}
};
} // anonymous namespace
template<
Format const& format,
typename... ArgP
>
constexpr bool
format_type_check() noexcept {
return format_type_check_impl<format, ArgP...>::g(
ElementType::esc == format.elements[0u].type
? format.next_serial_index(0u)
: 0u
);
}
namespace {
template<
Format const& format,
typename Arg
>
inline void
write_element(
std::ostream& stream,
Element const& element,
Arg&& arg
) {
using ios = std::ios_base;
ios::fmtflags stream_flags
= ios::showpoint
| ios::skipws
| ios::internal
;
switch (element.type) {
case ElementType::dec:
stream_flags |= ios::dec;
break;
case ElementType::hex:
stream_flags |= ios::hex;
break;
case ElementType::oct:
stream_flags |= ios::oct;
break;
case ElementType::flt:
stream_flags |= ios::fixed;
break;
case ElementType::boo:
stream_flags |= ios::boolalpha;
break;
case ElementType::ptr:
stream_flags |= ios::hex;
// TODO: OK default?
stream.width(sizeof(void*) << 1u);
break;
case ElementType::str:
break;
case ElementType::end:
case ElementType::esc:
break;
}
if (static_cast<unsigned>(ElementFlags::show_base) & element.flags) {
stream_flags |= ios::showbase;
}
if (static_cast<unsigned>(ElementFlags::show_sign) & element.flags) {
stream_flags |= ios::showpos;
}
if (static_cast<unsigned>(ElementFlags::zero_padded) & element.flags) {
stream.fill('0');
} else {
stream.fill(' ');
}
if (static_cast<unsigned>(ElementFlags::left_align) & element.flags) {
stream_flags |= ios::left;
}
if (0u != element.width) {
stream.width(static_cast<std::streamsize>(element.width));
}
stream.setf(
stream_flags,
ios::internal
| ios::skipws
| ios::showpos
| ios::showbase
| ios::showpoint
| ios::basefield
| ios::floatfield
| ios::adjustfield
| ios::fixed
| ios::boolalpha
);
stream <<
static_cast<typename type_to_element<Arg>::cast>(
std::forward<Arg>(arg)
)
;
stream.unsetf(stream_flags);
if (0u != element.width) {
stream.width(0u);
}
}
template<
Format const& format
>
inline void
stream_format_impl(
std::ostream& stream,
std::size_t const last_pos,
std::size_t const element_index
) {
Element const& element = format.elements[element_index];
stream.write(
format.string + last_pos,
static_cast<std::streamsize>(element.beg - last_pos)
+ (ElementType::esc == element.type)
);
if (ElementType::end != element.type) {
stream_format_impl<format>(
stream,
element.end,
element_index + 1u
);
}
}
template<
Format const& format,
typename ArgF,
typename... ArgP
>
inline void
stream_format_impl(
std::ostream& stream,
std::size_t const last_pos,
std::size_t const element_index,
ArgF&& front,
ArgP&&... args
) {
Element const& element = format.elements[element_index];
stream.write(
format.string + last_pos,
static_cast<std::streamsize>(element.beg - last_pos)
+ (ElementType::esc == element.type)
);
if (ElementType::esc == element.type) {
stream_format_impl<format>(
stream,
element.end,
element_index + 1u,
std::forward<ArgF>(front),
std::forward<ArgP>(args)...
);
} else {
write_element<format>(
stream,
element,
std::forward<ArgF>(front)
);
stream_format_impl<format>(
stream,
element.end,
element_index + 1u,
std::forward<ArgP>(args)...
);
}
}
} // anonymous namespace
template<
Format const& format,
typename... ArgP
>
void
stream_format(
std::ostream& stream,
ArgP&&... args
) {
static_assert(
sizeof...(ArgP) == format.serial_count,
"arguments do not match format"
);
static_assert(
format_type_check<format, ArgP...>(),
"type of argument does not match element in format"
);
stream_format_impl<format>(
stream,
0u,
0u,
std::forward<ArgP>(args)...
);
}
template<
Format const& format,
typename... ArgP
>
String
string_format(
ArgP&&... args
) {
StringStream stream;
stream_format<format>(stream, std::forward<ArgP>(args)...);
return stream.str();
}
namespace {
static constexpr Format const
//bad_length{"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"},
//bad_flag_1{"%-%"},
//bad_flag_2{"%+x"},
//bad_flag_3{"%#o"},
//bad_flag_4{"%#s"},
all{"%% %d %#x %#o %f %s"},
flags{"%d %#x %#o %f %b %#0p"},
max{"%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%"},
obj{"%s"},
empty{"empty"};
}
signed
main() {
std::cout
<< all << '\n'
<< flags << '\n'
<< max << '\n'
<< obj << '\n'
<< empty << '\n'
;
std::cout
<< string_format<all>(3, 0x12abcdef, 0777, 3.14f, "string") << '\n'
<< string_format<flags>(42, 42, 42, 42.0f, true, nullptr) << '\n'
<< string_format<max>() << '\n'
<< string_format<obj>(obj.elements[0u]) << '\n'
<< string_format<empty>() << '\n'
;
std::cout.flush();
}
@amikhailov1tv
Copy link

Oh my god

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment