[F] Fix incorrect behaviour in case of using standard iostream manipulators with fort::table.

This commit is contained in:
seleznevae 2020-04-21 20:27:32 +03:00
parent 51910b1552
commit 7844a5068f
4 changed files with 128 additions and 4 deletions

View File

@ -1,5 +1,9 @@
## v0.4.1
### Bug fixes
- Fix incorrect behaviour in case of using standard iostream manipulators with fort::table.
### Internal
- Add alias `libfort::fort` for `fort` target that can be used via `add_subdirectory` in cmake.

View File

@ -34,9 +34,11 @@ SOFTWARE.
#ifndef LIBFORT_HPP
#define LIBFORT_HPP
#include <iomanip>
#include <sstream>
#include <string>
#include <stdexcept>
#include <sstream>
#include <type_traits>
#include "fort.h"
@ -150,6 +152,41 @@ const table_manipulator endr(1);
*/
const table_manipulator separator(2);
// Utility functions for internal library usage.
namespace detail
{
template <typename T>
constexpr bool is_stream_manipulator_impl() noexcept
{
using Tdec = typename std::decay<T>::type;
// Manipulators for integral types
return std::is_same<Tdec, typename std::decay<decltype(std::dec)>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::hex)>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::oct)>::type>::value
// Floating-point manipulators
|| std::is_same<Tdec, typename std::decay<decltype(std::fixed)>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::scientific)>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::hexfloat)>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::defaultfloat)>::type>::value
// Misc
|| std::is_same<Tdec, typename std::decay<decltype(std::setbase(0))>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::setfill('\0'))>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::setprecision(0))>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::setw(0))>::type>::value;
}
}
/**
* Utility function that is used internally by the library to check if argument
* passed to operator<< is a manipulator. In case default behaviour is not
* enough write custom specialization of this function.
*/
template <typename T>
constexpr bool is_stream_manipulator() noexcept
{
return detail::is_stream_manipulator_impl<T>();
}
/**
* Property owner.
*
@ -534,8 +571,9 @@ public:
template <typename T>
table &operator<<(const T &arg)
{
constexpr bool is_manip = fort::is_stream_manipulator<typename std::decay<T>::type>();
stream_ << arg;
if (stream_.tellp() >= 0) {
if (stream_.tellp() >= 0 && !is_manip) {
#ifdef FT_HAVE_UTF8
if (TT == table_type::character) {
ft_nwrite(table_, 1, stream_.str().c_str());

View File

@ -34,9 +34,11 @@ SOFTWARE.
#ifndef LIBFORT_HPP
#define LIBFORT_HPP
#include <iomanip>
#include <sstream>
#include <string>
#include <stdexcept>
#include <sstream>
#include <type_traits>
#include "fort.h"
@ -150,6 +152,41 @@ const table_manipulator endr(1);
*/
const table_manipulator separator(2);
// Utility functions for internal library usage.
namespace detail
{
template <typename T>
constexpr bool is_stream_manipulator_impl() noexcept
{
using Tdec = typename std::decay<T>::type;
// Manipulators for integral types
return std::is_same<Tdec, typename std::decay<decltype(std::dec)>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::hex)>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::oct)>::type>::value
// Floating-point manipulators
|| std::is_same<Tdec, typename std::decay<decltype(std::fixed)>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::scientific)>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::hexfloat)>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::defaultfloat)>::type>::value
// Misc
|| std::is_same<Tdec, typename std::decay<decltype(std::setbase(0))>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::setfill('\0'))>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::setprecision(0))>::type>::value
|| std::is_same<Tdec, typename std::decay<decltype(std::setw(0))>::type>::value;
}
}
/**
* Utility function that is used internally by the library to check if argument
* passed to operator<< is a manipulator. In case default behaviour is not
* enough write custom specialization of this function.
*/
template <typename T>
constexpr bool is_stream_manipulator() noexcept
{
return detail::is_stream_manipulator_impl<T>();
}
/**
* Property owner.
*
@ -534,8 +571,9 @@ public:
template <typename T>
table &operator<<(const T &arg)
{
constexpr bool is_manip = fort::is_stream_manipulator<typename std::decay<T>::type>();
stream_ << arg;
if (stream_.tellp() >= 0) {
if (stream_.tellp() >= 0 && !is_manip) {
#ifdef FT_HAVE_UTF8
if (TT == table_type::character) {
ft_nwrite(table_, 1, stream_.str().c_str());

View File

@ -2,6 +2,16 @@
#include "fort.hpp"
#include "test_utils.hpp"
namespace fort
{
// Write custom specialisation for testing purposes.
using setw_type = typename std::decay<decltype(std::setw(0))>::type;
template <>
constexpr bool is_stream_manipulator<setw_type>() noexcept
{
return false;
}
}
void test_cpp_bug_fixes(void)
{
@ -97,6 +107,40 @@ void test_cpp_bug_fixes(void)
"+------+------+--------------------------------+\n";
assert_string_equal(table_str, table_str_etalon);
}
SCENARIO("Issue 49 - https://github.com/seleznevae/libfort/issues/49") {
{
fort::char_table table;
table << std::setprecision(5) << 3.14 << std::hex << 256 << fort::endr
<< std::fixed << std::setprecision(2) << 11.1234567;
std::string table_str = table.to_string();
std::string table_str_etalon =
"+-------+-----+\n"
"| | |\n"
"| 3.14 | 100 |\n"
"| | |\n"
"+-------+-----+\n"
"| | |\n"
"| 11.12 | |\n"
"| | |\n"
"+-------+-----+\n";
assert_string_equal(table_str, table_str_etalon);
}
{
fort::char_table table;
table << std::setw(15) << 3.14000001;
std::string table_str = table.to_string();
std::string table_str_etalon =
"+--+-----------------+\n"
"| | |\n"
"| | 3.14 |\n"
"| | |\n"
"+--+-----------------+\n";
assert_string_equal(table_str, table_str_etalon);
}
}
}
void test_cpp_table_basic(void)