format: add trivial printf wrapper

This commit is contained in:
Danny Robson 2015-07-21 01:37:45 +10:00
parent f29e77c795
commit f451e5555e
5 changed files with 191 additions and 0 deletions

View File

@ -51,6 +51,9 @@ UTIL_FILES = \
fixed.hpp \ fixed.hpp \
float.cpp \ float.cpp \
float.hpp \ float.hpp \
format.cpp \
format.hpp \
format.ipp \
fourcc.cpp \ fourcc.cpp \
fourcc.hpp \ fourcc.hpp \
guid.cpp \ guid.cpp \
@ -315,6 +318,7 @@ TEST_BIN = \
test/extent \ test/extent \
test/fixed \ test/fixed \
test/float \ test/float \
test/format \
test/hash/murmur \ test/hash/murmur \
test/hash/fasthash \ test/hash/fasthash \
test/hmac \ test/hmac \

26
format.cpp Normal file
View File

@ -0,0 +1,26 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#include "format.hpp"
#include <utility>
//std::string
//util::format (std::string &&fmt)
//{
// return std::move (fmt);
//}

47
format.hpp Normal file
View File

@ -0,0 +1,47 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_FORMAT_HPP
#define __UTIL_FORMAT_HPP
#include <stdexcept>
#include <string>
namespace util {
namespace format {
template <typename ...Args>
std::string render (const std::string &fmt, Args&&...);
class error : public std::runtime_error
{ using runtime_error::runtime_error; };
// value-specifier mismatch
class value_error : public error
{ using error::error; };
// malformed format specifier
class format_error : public error
{ using error::error; };
// missing format specifier
class missing_error : public error
{ using error::error; };
}
}
#include "format.ipp"
#endif

94
format.ipp Normal file
View File

@ -0,0 +1,94 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#if defined(__UTIL_FORMAT_IPP)
#error
#endif
#define __UTIL_FORMAT_IPP
#include "debug.hpp"
#include <algorithm>
#include <sstream>
#include <stdexcept>
#include <iterator>
namespace util {
namespace detail { namespace format {
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));
}
template <typename InputIt,
typename ValueT,
typename ...Args>
void
render (InputIt first,
InputIt last,
std::ostringstream &dest,
const ValueT& val,
Args&& ...args)
{
CHECK (first <= last);
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)
throw util::format::format_error ("missing format specifier");
if (*spec != 's')
throw util::format::format_error ("unhandled format specifier");
dest << val;
render (spec + 1, last, dest, std::forward<Args> (args)...);
}
} }
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 ();
}
}
}

20
test/format.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "format.hpp"
#include "tap.hpp"
int
main (void)
{
using namespace std::string_literals;
util::TAP::logger tap;
tap.expect_eq (util::format::render ("identity"), "identity"s, "identity literal");
tap.expect_eq (util::format::render ("%s", "identity"s), "identity"s, "identity substitution");
tap.expect_throw<util::format::missing_error> ([] (void) { util::format::render ("%s"); });
tap.expect_throw<util::format::format_error> ([] (void) { util::format::render ("%!", 42); });
tap.expect_throw<util::format::format_error> ([] (void) { util::format::render ("%", 42); });
return tap.status ();
}