| #ifndef TEST_SUPPORT_FORMAT_STRING_H |
| #define TEST_SUPPORT_FORMAT_STRING_H |
| |
| #include <cstdio> |
| #include <string> |
| #include <memory> |
| #include <array> |
| #include <cstdarg> |
| |
| namespace format_string_detail { |
| inline std::string format_string_imp(const char* msg, ...) { |
| // we might need a second shot at this, so pre-emptivly make a copy |
| struct GuardVAList { |
| va_list& xtarget; |
| bool active; |
| GuardVAList(va_list& val) : xtarget(val), active(true) {} |
| |
| void clear() { |
| if (active) |
| va_end(xtarget); |
| active = false; |
| } |
| ~GuardVAList() { |
| if (active) |
| va_end(xtarget); |
| } |
| }; |
| va_list args; |
| va_start(args, msg); |
| GuardVAList args_guard(args); |
| |
| va_list args_cp; |
| va_copy(args_cp, args); |
| GuardVAList args_copy_guard(args_cp); |
| |
| std::array<char, 256> local_buff; |
| std::size_t size = local_buff.size(); |
| auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp); |
| |
| args_copy_guard.clear(); |
| |
| // handle empty expansion |
| if (ret == 0) |
| return std::string{}; |
| if (static_cast<std::size_t>(ret) < size) |
| return std::string(local_buff.data()); |
| |
| // we did not provide a long enough buffer on our first attempt. |
| // add 1 to size to account for null-byte in size cast to prevent overflow |
| size = static_cast<std::size_t>(ret) + 1; |
| auto buff_ptr = std::unique_ptr<char[]>(new char[size]); |
| ret = ::vsnprintf(buff_ptr.get(), size, msg, args); |
| return std::string(buff_ptr.get()); |
| } |
| |
| const char* unwrap(std::string& s) { return s.c_str(); } |
| template <class Arg> |
| Arg const& unwrap(Arg& a) { |
| static_assert(!std::is_class<Arg>::value, "cannot pass class here"); |
| return a; |
| } |
| |
| } // namespace format_string_detail |
| |
| template <class... Args> |
| std::string format_string(const char* fmt, Args const&... args) { |
| return format_string_detail::format_string_imp( |
| fmt, format_string_detail::unwrap(const_cast<Args&>(args))...); |
| } |
| |
| #endif // TEST_SUPPORT_FORMAT_STRING_HPP |