diff --git a/ChangeLog.md b/ChangeLog.md index 79585e8..73ed4aa 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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. diff --git a/lib/fort.hpp b/lib/fort.hpp index bcd5ea1..7933c65 100644 --- a/lib/fort.hpp +++ b/lib/fort.hpp @@ -34,9 +34,11 @@ SOFTWARE. #ifndef LIBFORT_HPP #define LIBFORT_HPP +#include +#include #include #include -#include +#include #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 +constexpr bool is_stream_manipulator_impl() noexcept +{ + using Tdec = typename std::decay::type; + // Manipulators for integral types + return std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + // Floating-point manipulators + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + // Misc + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::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 +constexpr bool is_stream_manipulator() noexcept +{ + return detail::is_stream_manipulator_impl(); +} + /** * Property owner. * @@ -534,8 +571,9 @@ public: template table &operator<<(const T &arg) { + constexpr bool is_manip = fort::is_stream_manipulator::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()); diff --git a/src/fort.hpp b/src/fort.hpp index bcd5ea1..7933c65 100644 --- a/src/fort.hpp +++ b/src/fort.hpp @@ -34,9 +34,11 @@ SOFTWARE. #ifndef LIBFORT_HPP #define LIBFORT_HPP +#include +#include #include #include -#include +#include #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 +constexpr bool is_stream_manipulator_impl() noexcept +{ + using Tdec = typename std::decay::type; + // Manipulators for integral types + return std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + // Floating-point manipulators + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + // Misc + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::type>::value + || std::is_same::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 +constexpr bool is_stream_manipulator() noexcept +{ + return detail::is_stream_manipulator_impl(); +} + /** * Property owner. * @@ -534,8 +571,9 @@ public: template table &operator<<(const T &arg) { + constexpr bool is_manip = fort::is_stream_manipulator::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()); diff --git a/tests/bb_tests_cpp/test_table_basic.cpp b/tests/bb_tests_cpp/test_table_basic.cpp index 4e52738..e45cae4 100644 --- a/tests/bb_tests_cpp/test_table_basic.cpp +++ b/tests/bb_tests_cpp/test_table_basic.cpp @@ -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::type; +template <> +constexpr bool is_stream_manipulator() 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)