1 #ifndef TEST_SUPPORT_FORMAT_STRING_H 2 #define TEST_SUPPORT_FORMAT_STRING_H 3 4 #include <cstdio> 5 #include <string> 6 #include <memory> 7 #include <array> 8 #include <cstdarg> 9 10 namespace format_string_detail { 11 inline std::string format_string_imp(const char* msg, ...) { 12 // we might need a second shot at this, so pre-emptivly make a copy 13 struct GuardVAList { 14 va_list& xtarget; 15 bool active; 16 GuardVAList(va_list& val) : xtarget(val), active(true) {} 17 18 void clear() { 19 if (active) 20 va_end(xtarget); 21 active = false; 22 } 23 ~GuardVAList() { 24 if (active) 25 va_end(xtarget); 26 } 27 }; 28 va_list args; 29 va_start(args, msg); 30 GuardVAList args_guard(args); 31 32 va_list args_cp; 33 va_copy(args_cp, args); 34 GuardVAList args_copy_guard(args_cp); 35 36 std::array<char, 256> local_buff; 37 std::size_t size = local_buff.size(); 38 auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp); 39 40 args_copy_guard.clear(); 41 42 // handle empty expansion 43 if (ret == 0) 44 return std::string{}; 45 if (static_cast<std::size_t>(ret) < size) 46 return std::string(local_buff.data()); 47 48 // we did not provide a long enough buffer on our first attempt. 49 // add 1 to size to account for null-byte in size cast to prevent overflow 50 size = static_cast<std::size_t>(ret) + 1; 51 auto buff_ptr = std::unique_ptr<char[]>(new char[size]); 52 ret = ::vsnprintf(buff_ptr.get(), size, msg, args); 53 return std::string(buff_ptr.get()); 54 } 55 56 const char* unwrap(std::string& s) { return s.c_str(); } 57 template <class Arg> 58 Arg const& unwrap(Arg& a) { 59 static_assert(!std::is_class<Arg>::value, "cannot pass class here"); 60 return a; 61 } 62 63 } // namespace format_string_detail 64 65 template <class... Args> 66 std::string format_string(const char* fmt, Args const&... args) { 67 return format_string_detail::format_string_imp( 68 fmt, format_string_detail::unwrap(const_cast<Args&>(args))...); 69 } 70 71 #endif // TEST_SUPPORT_FORMAT_STRING_HPP 72