2015-07-21 01:37:45 +10:00
|
|
|
/*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*
|
2016-04-27 16:08:28 +10:00
|
|
|
* Copyright 2015-2016 Danny Robson <danny@nerdcruft.net>
|
2015-07-21 01:37:45 +10:00
|
|
|
*/
|
|
|
|
|
|
|
|
#if defined(__UTIL_FORMAT_IPP)
|
|
|
|
#error
|
|
|
|
#endif
|
|
|
|
#define __UTIL_FORMAT_IPP
|
|
|
|
|
|
|
|
#include "debug.hpp"
|
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <sstream>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <iterator>
|
2016-01-20 16:36:32 +11:00
|
|
|
#include <boost/filesystem/path.hpp>
|
2015-07-21 01:37:45 +10:00
|
|
|
|
|
|
|
namespace util {
|
|
|
|
namespace detail { namespace format {
|
2015-09-21 16:34:15 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
template <typename T>
|
|
|
|
inline bool
|
2016-02-02 16:49:45 +11:00
|
|
|
is_type_specifier (const char*)
|
2015-09-21 16:34:15 +10:00
|
|
|
{ return false; }
|
|
|
|
|
|
|
|
|
2016-04-27 16:08:28 +10:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
template <>
|
|
|
|
inline bool
|
|
|
|
is_type_specifier<char> (const char *s)
|
|
|
|
{ return *s == 'c'; }
|
|
|
|
|
|
|
|
|
2015-09-21 16:34:15 +10:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
template <>
|
|
|
|
inline bool
|
2016-02-02 16:49:45 +11:00
|
|
|
is_type_specifier<const char*> (const char *s)
|
2015-09-21 16:34:15 +10:00
|
|
|
{ return *s == 's'; }
|
|
|
|
|
2016-01-20 16:36:32 +11:00
|
|
|
|
2015-09-21 16:34:15 +10:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
template <>
|
|
|
|
inline bool
|
2016-02-02 16:49:45 +11:00
|
|
|
is_type_specifier<char*> (const char *s)
|
2015-09-21 16:34:15 +10:00
|
|
|
{ return *s == 's'; }
|
|
|
|
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
template <>
|
|
|
|
inline bool
|
2016-02-02 16:49:45 +11:00
|
|
|
is_type_specifier<std::string> (const char *s)
|
2015-09-21 16:34:15 +10:00
|
|
|
{ return *s == 's'; }
|
|
|
|
|
|
|
|
|
2016-01-20 16:36:32 +11:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
template <>
|
|
|
|
inline bool
|
2016-02-02 16:49:45 +11:00
|
|
|
is_type_specifier<boost::filesystem::path> (const char *s)
|
2016-01-20 16:36:32 +11:00
|
|
|
{ return *s == 's'; }
|
|
|
|
|
|
|
|
|
2015-09-21 16:34:15 +10:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
template <>
|
|
|
|
inline bool
|
2016-02-02 16:49:45 +11:00
|
|
|
is_type_specifier<size_t> (const char *s)
|
2016-04-14 18:54:52 +10:00
|
|
|
{ return *s == 'u' || *s == 'x'; }
|
2015-09-21 16:34:15 +10:00
|
|
|
|
|
|
|
|
2016-01-28 12:58:31 +11:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
template <>
|
|
|
|
inline bool
|
2016-02-02 16:49:45 +11:00
|
|
|
is_type_specifier<unsigned> (const char *s)
|
2016-04-14 18:54:52 +10:00
|
|
|
{ return *s == 'u' || *s == 'x'; }
|
2016-01-28 12:58:31 +11:00
|
|
|
|
|
|
|
|
2016-02-02 16:50:28 +11:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
template <>
|
|
|
|
inline bool
|
|
|
|
is_type_specifier<int> (const char *s)
|
|
|
|
{
|
2016-04-14 18:54:52 +10:00
|
|
|
return *s == 'i' || *s == 'd' || *s == 'x';
|
2016-02-02 16:50:28 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-28 12:58:31 +11:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
template <>
|
|
|
|
inline bool
|
2016-02-02 16:49:45 +11:00
|
|
|
is_type_specifier<float> (const char *s)
|
2016-01-28 12:58:31 +11:00
|
|
|
{
|
|
|
|
switch (*s) {
|
|
|
|
case 'e':
|
|
|
|
case 'E':
|
|
|
|
case 'f':
|
|
|
|
case 'F':
|
|
|
|
case 'g':
|
|
|
|
case 'G':
|
|
|
|
case 'a':
|
|
|
|
case 'A':
|
|
|
|
return true;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-02 16:49:45 +11:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
template <typename T>
|
|
|
|
inline bool
|
|
|
|
is_valid_specifier (const char *s)
|
|
|
|
{
|
|
|
|
return *s == '!' || is_type_specifier<T> (s);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-21 16:34:15 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////
|
2015-07-21 01:37:45 +10:00
|
|
|
template <typename InputIt>
|
|
|
|
void
|
|
|
|
render (InputIt first,
|
|
|
|
InputIt last,
|
|
|
|
std::ostringstream &dest)
|
|
|
|
{
|
|
|
|
static const char DELIMITER = '%';
|
|
|
|
if (std::find (first, last, DELIMITER) != last)
|
|
|
|
throw util::format::missing_error ("format specifier without value");
|
|
|
|
|
|
|
|
std::copy (first, last, std::ostream_iterator<char> (dest));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-21 16:34:15 +10:00
|
|
|
//---------------------------------------------------------------------
|
2015-07-21 01:37:45 +10:00
|
|
|
template <typename InputIt,
|
|
|
|
typename ValueT,
|
|
|
|
typename ...Args>
|
|
|
|
void
|
|
|
|
render (InputIt first,
|
|
|
|
InputIt last,
|
|
|
|
std::ostringstream &dest,
|
|
|
|
const ValueT& val,
|
|
|
|
Args&& ...args)
|
|
|
|
{
|
|
|
|
CHECK (first <= last);
|
|
|
|
|
2016-04-28 16:07:49 +10:00
|
|
|
using namespace std::literals;
|
|
|
|
|
2015-07-21 01:37:45 +10:00
|
|
|
static const char DELIMITER = '%';
|
|
|
|
auto cursor = std::find (first, last, DELIMITER);
|
|
|
|
std::copy (first, cursor, std::ostream_iterator<char> (dest));
|
|
|
|
|
|
|
|
if (cursor == last)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto spec = cursor + 1;
|
|
|
|
if (spec == last)
|
2016-04-28 16:07:49 +10:00
|
|
|
throw util::format::format_error ("missing format specifier"s);
|
2015-07-21 01:37:45 +10:00
|
|
|
|
2015-09-21 16:34:15 +10:00
|
|
|
if (!is_valid_specifier<typename std::decay<ValueT>::type> (&*spec))
|
2016-04-27 16:12:24 +10:00
|
|
|
throw util::format::invalid_specifier<ValueT> (*spec);
|
2015-07-21 01:37:45 +10:00
|
|
|
|
2016-04-14 18:54:52 +10:00
|
|
|
if (*spec == 'x') {
|
|
|
|
dest << std::hex << val << std::dec;
|
|
|
|
} else
|
|
|
|
dest << val;
|
2015-07-21 01:37:45 +10:00
|
|
|
|
|
|
|
render (spec + 1, last, dest, std::forward<Args> (args)...);
|
|
|
|
}
|
|
|
|
} }
|
|
|
|
|
2015-09-21 16:34:15 +10:00
|
|
|
///////////////////////////////////////////////////////////////////////////
|
2015-07-21 01:37:45 +10:00
|
|
|
namespace format {
|
|
|
|
template <typename ...Args>
|
|
|
|
std::string
|
|
|
|
render (const std::string &fmt, Args&&... args)
|
|
|
|
{
|
|
|
|
std::ostringstream out;
|
|
|
|
|
|
|
|
util::detail::format::render (
|
|
|
|
fmt.begin (),
|
|
|
|
fmt.end (),
|
|
|
|
out,
|
|
|
|
std::forward<Args> (args)...
|
|
|
|
);
|
|
|
|
|
|
|
|
return out.str ();
|
|
|
|
}
|
|
|
|
}
|
2016-04-27 16:12:24 +10:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
// TODO: we'd like to use typeid here for type naming, but we don't allow
|
|
|
|
// RTTI. revisit this when introspection is more advanced.
|
|
|
|
template <typename ValueT>
|
|
|
|
format::invalid_specifier<ValueT>::invalid_specifier (char _specifier):
|
|
|
|
format_error (
|
|
|
|
format::render ("invalid specifier '%c' for type '%s'",
|
|
|
|
_specifier,
|
|
|
|
"unimplemented")
|
|
|
|
),
|
|
|
|
m_specifier (_specifier)
|
|
|
|
{ ; }
|
|
|
|
|
|
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
template <typename ValueT>
|
|
|
|
char
|
|
|
|
format::invalid_specifier<ValueT>::specifier (void) const
|
|
|
|
{ return m_specifier; }
|
2015-07-21 01:37:45 +10:00
|
|
|
}
|