Skip to content

Commit

Permalink
(draft)
Browse files Browse the repository at this point in the history
  • Loading branch information
K-ballo committed Nov 18, 2017
1 parent 051b74e commit ee05ea1
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 16 deletions.
149 changes: 133 additions & 16 deletions hpx/util/format.hpp
Expand Up @@ -3,42 +3,159 @@
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// hpxinspect:nodeprecatedinclude:boost/format.hpp
// hpxinspect:nodeprecatedname:boost::format

#ifndef HPX_UTIL_FORMAT_HPP
#define HPX_UTIL_FORMAT_HPP

#include <hpx/config.hpp>

#include <boost/format.hpp>
#include <boost/utility/string_ref.hpp>

#include <iosfwd>
#include <cctype>
#include <cstddef>
#include <cstdio>
#include <ostream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <vector>

namespace hpx { namespace util
{
namespace detail
{
///////////////////////////////////////////////////////////////////////////
template <typename T>
struct type_specifier
{
static char const* value() noexcept;
};

# define DECL_TYPE_SPECIFIER(Type, Spec) \
template <> struct type_specifier<Type> \
{ static char const* value() noexcept { return #Spec; } } \
/**/

DECL_TYPE_SPECIFIER(char, c);
DECL_TYPE_SPECIFIER(wchar_t, lc);

DECL_TYPE_SPECIFIER(signed char, hhd);
DECL_TYPE_SPECIFIER(short, hd);
DECL_TYPE_SPECIFIER(int, d);
DECL_TYPE_SPECIFIER(long, ld);
DECL_TYPE_SPECIFIER(long long, lld);

DECL_TYPE_SPECIFIER(unsigned char, hhu);
DECL_TYPE_SPECIFIER(unsigned short, hu);
DECL_TYPE_SPECIFIER(unsigned int, u);
DECL_TYPE_SPECIFIER(unsigned long, lu);
DECL_TYPE_SPECIFIER(unsigned long long, llu);

DECL_TYPE_SPECIFIER(float, f);
DECL_TYPE_SPECIFIER(double, lf);
DECL_TYPE_SPECIFIER(long double, Lf);

template <typename T>
struct type_specifier<T*>
{
static char const* value() noexcept { return "p"; }
};

# undef DECL_TYPE_SPECIFIER

///////////////////////////////////////////////////////////////////////
template <typename T, bool IsFundamental = std::is_fundamental<T>::value>
struct formatter
{
static void call(
std::ostream& os, boost::string_ref spec, void const* ptr)
{
// conversion specifier
char const* conv_spec = "";
if (spec.empty() || !std::isalpha(spec.back()))
conv_spec = type_specifier<T>::value();

// copy spec to a null terminated buffer
char format[16];
std::sprintf(format, "%%%.*s%s",
(int)spec.size(), spec.data(), conv_spec);

T const& value = *static_cast<T const*>(ptr);
std::size_t const length = std::snprintf(nullptr, 0, format, value);
std::vector<char> buffer(length + 1);
std::snprintf(buffer.data(), length + 1, format, value);

os << boost::string_ref(buffer.data(), length);
}
};

template <>
struct formatter<bool>; // missing

template <typename T>
void format_value(std::ostream& os, boost::string_ref spec, T const& value)
{
if (!spec.empty())
throw std::runtime_error("Not a valid format specifier");

os << value;
}

template <typename T>
struct formatter<T, /*IsFundamental=*/false>
{
static void call(
std::ostream& os, boost::string_ref spec, void const* value)
{
// ADL customization point
format_value(os, spec, *static_cast<T const*>(value));
}
};

struct format_arg
{
template <typename T>
format_arg(T const& arg)
: _data(&arg)
, _formatter(&detail::formatter<T>::call)
{}

void operator()(std::ostream& os, boost::string_ref spec) const
{
_formatter(os, spec, _data);
}

void const* _data;
void (*_formatter)(std::ostream&, boost::string_ref spec, void const*);
};

///////////////////////////////////////////////////////////////////////
HPX_EXPORT void format_to(
std::ostream& os,
boost::string_ref format_str,
format_arg const* args, std::size_t count);

HPX_EXPORT std::string format(
boost::string_ref format_str,
format_arg const* args, std::size_t count);
}

template <typename ...Args>
std::string format(
std::string const& format_str, Args const&... args)
boost::string_ref format_str, Args const&... args)
{
boost::format fmt(format_str);
int const _sequencer[] = { ((fmt % args), 0)... };
(void)_sequencer;

return boost::str(fmt);
detail::format_arg const format_args[] = { args..., 0 };
return detail::format(format_str, format_args, sizeof...(Args));
}

template <typename ...Args>
std::ostream& format_to(
std::ostream& os,
std::string const& format_str, Args const&... args)
boost::string_ref format_str, Args const&... args)
{
boost::format fmt(format_str);
int const _sequencer[] = { ((fmt % args), 0)... };
(void)_sequencer;
detail::format_arg const format_args[] = { args..., 0 };
detail::format_to(os, format_str, format_args, sizeof...(Args));

return os << fmt;
return os;
}
}}

Expand Down
Expand Up @@ -14,6 +14,7 @@
#include <cstring>
#include <string>

#include <windows.h> // this must go before psapi.h
#include <psapi.h>

namespace hpx { namespace performance_counters { namespace memory
Expand Down
116 changes: 116 additions & 0 deletions src/util/format.cpp
@@ -0,0 +1,116 @@
// Copyright (c) 2017 Agustin Berge
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <hpx/util/format.hpp>

#include <boost/utility/string_ref.hpp>

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <limits>
#include <ostream>
#include <sstream>
#include <string>

namespace hpx { namespace util { namespace detail
{
///////////////////////////////////////////////////////////////////////////
static unsigned long long format_atoi(
boost::string_ref str, std::size_t* pos = nullptr) noexcept
{
// copy input to a null terminated buffer
static constexpr std::size_t digits10 =
std::numeric_limits<unsigned long long>::digits10 + 1;
char buffer[digits10 + 1] = {};
std::copy_n(str.data(), (std::min)(str.size(), digits10), buffer);

char const* first = buffer;
char* last = buffer;
unsigned long long r = std::strtoull(first, &last, 10);
if (pos != nullptr)
*pos = last - first;
return r;
}

static std::size_t format_find(
boost::string_ref str, char c) noexcept
{
std::size_t r = 0;
std::size_t const size = str.size();
while (r < size && str[r] != c) ++r;
return r;
}

static boost::string_ref format_substr(
boost::string_ref str,
std::size_t start, std::size_t end = boost::string_ref::npos) noexcept
{
return start < end && start < str.size()
? str.substr(start, end - start) : boost::string_ref{};
}

///////////////////////////////////////////////////////////////////////////
// replacement-field ::= '{' [arg-id] [':' format-spec] '}'
struct format_field
{
std::size_t arg_id;
boost::string_ref spec;
};

static format_field parse_field(boost::string_ref field) noexcept
{
std::size_t const sep = format_find(field, ':');
boost::string_ref const arg_id = format_substr(field, 0, sep);
boost::string_ref const spec = format_substr(field, sep + 1);

std::size_t const id = format_atoi(arg_id) - 1;
return format_field{id, spec};
}

void format_to(
std::ostream& os,
boost::string_ref format_str,
format_arg const* args, std::size_t count)
{
std::size_t index = 0;
while (!format_str.empty())
{
if (format_str[0] == '{')
{
assert(!format_str.empty());
if (format_str[1] == '{')
{
os << '{';
} else {
std::size_t const end = format_find(format_str, '}');
boost::string_ref field_str = format_substr(format_str, 1, end);
format_field const field = parse_field(field_str);
format_str.remove_prefix(end - 1);

std::size_t const id = field.arg_id == -1 ? index : field.arg_id;
args[id](os, field.spec);
++index;
}
format_str.remove_prefix(2);
} else {
std::size_t const next = format_find(format_str, '{');

os << format_str.substr(0, next);
format_str.remove_prefix(next);
}
}
}

std::string format(
boost::string_ref format_str,
format_arg const* args, std::size_t count)
{
std::ostringstream os;
detail::format_to(os, format_str, args, count);
return os.str();
}
}}}

0 comments on commit ee05ea1

Please sign in to comment.