1 #ifndef TEST_SUPPORT_FORMAT_STRING_H
2 #define TEST_SUPPORT_FORMAT_STRING_H
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-emptively make a copy
16 GuardVAList(va_list& val
) : xtarget(val
), active(true) {}
30 GuardVAList
args_guard(args
);
33 va_copy(args_cp
, args
);
34 GuardVAList
args_copy_guard(args_cp
);
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
);
40 args_copy_guard
.clear();
42 // handle empty expansion
45 if (static_cast<std::size_t>(ret
) < size
)
46 return std::string(local_buff
.data());
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());
56 const char* unwrap(std::string
& s
) { return s
.c_str(); }
58 Arg
const& unwrap(Arg
& a
) {
59 static_assert(!std::is_class
<Arg
>::value
, "cannot pass class here");
63 } // namespace format_string_detail
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
))...);
71 #endif // TEST_SUPPORT_FORMAT_STRING_HPP