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