• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
format_string_imp(const char * msg,...)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 
unwrap(std::string & s)56 const char* unwrap(std::string& s) { return s.c_str(); }
57 template <class Arg>
unwrap(Arg & a)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>
format_string(const char * fmt,Args const &...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