diff options
Diffstat (limited to 'test/support/format_string.hpp')
-rw-r--r-- | test/support/format_string.hpp | 71 |
1 files changed, 71 insertions, 0 deletions
diff --git a/test/support/format_string.hpp b/test/support/format_string.hpp new file mode 100644 index 000000000000..44dc30f551d1 --- /dev/null +++ b/test/support/format_string.hpp @@ -0,0 +1,71 @@ +#ifndef TEST_SUPPORT_FORMAT_STRING_HPP +#define TEST_SUPPORT_FORMAT_STRING_HPP + +#include <cstdio> +#include <string> +#include <memory> +#include <array> +#include <cstdarg> + +namespace format_string_detail { +inline std::string format_string_imp(const char* msg, ...) { + // we might need a second shot at this, so pre-emptivly make a copy + struct GuardVAList { + va_list& xtarget; + bool active; + GuardVAList(va_list& val) : xtarget(val), active(true) {} + + void clear() { + if (active) + va_end(xtarget); + active = false; + } + ~GuardVAList() { + if (active) + va_end(xtarget); + } + }; + va_list args; + va_start(args, msg); + GuardVAList args_guard(args); + + va_list args_cp; + va_copy(args_cp, args); + GuardVAList args_copy_guard(args_cp); + + std::array<char, 256> local_buff; + std::size_t size = local_buff.size(); + auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp); + + args_copy_guard.clear(); + + // handle empty expansion + if (ret == 0) + return std::string{}; + if (static_cast<std::size_t>(ret) < size) + return std::string(local_buff.data()); + + // we did not provide a long enough buffer on our first attempt. + // add 1 to size to account for null-byte in size cast to prevent overflow + size = static_cast<std::size_t>(ret) + 1; + auto buff_ptr = std::unique_ptr<char[]>(new char[size]); + ret = ::vsnprintf(buff_ptr.get(), size, msg, args); + return std::string(buff_ptr.get()); +} + +const char* unwrap(std::string& s) { return s.c_str(); } +template <class Arg> +Arg const& unwrap(Arg& a) { + static_assert(!std::is_class<Arg>::value, "cannot pass class here"); + return a; +} + +} // namespace format_string_detail + +template <class... Args> +std::string format_string(const char* fmt, Args const&... args) { + return format_string_detail::format_string_imp( + fmt, format_string_detail::unwrap(const_cast<Args&>(args))...); +} + +#endif // TEST_SUPPORT_FORMAT_STRING_HPP |