aboutsummaryrefslogtreecommitdiff
path: root/test/support/format_string.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/support/format_string.hpp')
-rw-r--r--test/support/format_string.hpp71
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