format_string.h 1.9 KB

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