summaryrefslogtreecommitdiffstats
path: root/libcxx/test
diff options
context:
space:
mode:
authorEric Fiselier <eric@efcs.ca>2018-07-23 02:00:52 +0000
committerEric Fiselier <eric@efcs.ca>2018-07-23 02:00:52 +0000
commit9158bfd32ebd457476bc367707fcf391ca0520f1 (patch)
tree5669f4d5e87703dfcf8ab10a6e1eeeba05ecab0d /libcxx/test
parentaa87753097b1302d268a4f27ee1251f065379f56 (diff)
downloadbcm5719-llvm-9158bfd32ebd457476bc367707fcf391ca0520f1.tar.gz
bcm5719-llvm-9158bfd32ebd457476bc367707fcf391ca0520f1.zip
Implement filesystem_error::what() and improve reporting.
This patch implements the `what()` for filesystem errors. The message includes the 'what_arg', any paths that were specified, and the error code message. Additionally this patch refactors how errors are created, making it easier to report them correctly. llvm-svn: 337664
Diffstat (limited to 'libcxx/test')
-rw-r--r--libcxx/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp3
-rw-r--r--libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp6
-rw-r--r--libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp16
-rw-r--r--libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp15
-rw-r--r--libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp15
-rw-r--r--libcxx/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp19
-rw-r--r--libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp4
-rw-r--r--libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp5
-rw-r--r--libcxx/test/support/filesystem_test_helper.hpp77
-rw-r--r--libcxx/test/support/format_string.hpp68
10 files changed, 184 insertions, 44 deletions
diff --git a/libcxx/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp b/libcxx/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp
index 30b44b877c5..2305f54f5cb 100644
--- a/libcxx/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp
+++ b/libcxx/test/libcxx/experimental/filesystem/class.directory_entry/directory_entry.mods/last_write_time.sh.cpp
@@ -80,7 +80,8 @@ TEST_CASE(last_write_time_not_representable_error) {
TEST_CHECK(last_write_time(file, ec) == file_time_type::min());
TEST_CHECK(ErrorIs(ec, expected_err));
- ExceptionChecker CheckExcept(file, expected_err);
+ ExceptionChecker CheckExcept(file, expected_err,
+ "directory_entry::last_write_time");
TEST_CHECK_THROW_RESULT(fs::filesystem_error, CheckExcept,
ent.last_write_time());
diff --git a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp
index 40d2cef1da1..74c73f030c9 100644
--- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp
+++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.mods/refresh.pass.cpp
@@ -168,7 +168,8 @@ TEST_CASE(refresh_cannot_resolve) {
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
TEST_CHECK(ent.path() == file);
- ExceptionChecker Checker(file, std::errc::permission_denied);
+ ExceptionChecker Checker(file, std::errc::permission_denied,
+ "directory_entry::refresh");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
}
permissions(dir, old_perms);
@@ -182,7 +183,8 @@ TEST_CASE(refresh_cannot_resolve) {
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
TEST_CHECK(ent.path() == sym_in_dir);
- ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
+ ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
+ "directory_entry::refresh");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
}
permissions(dir, old_perms);
diff --git a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp
index d5050edc71f..a38b10c00fc 100644
--- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp
+++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/file_size.pass.cpp
@@ -102,7 +102,7 @@ TEST_CASE(not_regular_file) {
TEST_CHECK(ec == other_ec);
TEST_CHECK(ErrorIs(ec, TC.expected_err));
- ExceptionChecker Checker(p, TC.expected_err);
+ ExceptionChecker Checker(p, TC.expected_err, "directory_entry::file_size");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
}
}
@@ -134,7 +134,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::DNE,
- std::errc::no_such_file_or_directory);
+ std::errc::no_such_file_or_directory,
+ "directory_entry::file_size");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
}
// test a dead symlink
@@ -156,7 +157,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::BadSymlink,
- std::errc::no_such_file_or_directory);
+ std::errc::no_such_file_or_directory,
+ "directory_entry::file_size");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
}
// test a file w/o appropriate permissions.
@@ -174,7 +176,7 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
- ExceptionChecker Checker(file, std::errc::permission_denied);
+ ExceptionChecker Checker(file, std::errc::permission_denied, "file_size");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
permissions(dir, old_perms);
@@ -199,7 +201,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
- ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
+ ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
+ "file_size");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
permissions(dir, old_perms);
@@ -224,7 +227,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
- ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
+ ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied,
+ "file_size");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
permissions(dir, old_perms);
diff --git a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
index 0ec801bb4bf..d11457d20ae 100644
--- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
+++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/hard_link_count.pass.cpp
@@ -133,7 +133,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::DNE,
- std::errc::no_such_file_or_directory);
+ std::errc::no_such_file_or_directory,
+ "directory_entry::hard_link_count");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
}
// test a dead symlink
@@ -155,7 +156,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::BadSymlink,
- std::errc::no_such_file_or_directory);
+ std::errc::no_such_file_or_directory,
+ "directory_entry::hard_link_count");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
}
// test a file w/o appropriate permissions.
@@ -173,7 +175,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
- ExceptionChecker Checker(file, std::errc::permission_denied);
+ ExceptionChecker Checker(file, std::errc::permission_denied,
+ "hard_link_count");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
permissions(dir, old_perms);
@@ -198,7 +201,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
- ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
+ ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
+ "hard_link_count");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
permissions(dir, old_perms);
@@ -223,7 +227,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
- ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
+ ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied,
+ "hard_link_count");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
permissions(dir, old_perms);
diff --git a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
index 250dc6f5a11..3d519728c50 100644
--- a/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
+++ b/libcxx/test/std/experimental/filesystem/class.directory_entry/directory_entry.obs/last_write_time.pass.cpp
@@ -106,7 +106,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::DNE,
- std::errc::no_such_file_or_directory);
+ std::errc::no_such_file_or_directory,
+ "directory_entry::last_write_time");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
}
// test a dead symlink
@@ -128,7 +129,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
ExceptionChecker Checker(StaticEnv::BadSymlink,
- std::errc::no_such_file_or_directory);
+ std::errc::no_such_file_or_directory,
+ "directory_entry::last_write_time");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
}
// test a file w/o appropriate permissions.
@@ -146,7 +148,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
- ExceptionChecker Checker(file, std::errc::permission_denied);
+ ExceptionChecker Checker(file, std::errc::permission_denied,
+ "last_write_time");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
permissions(dir, old_perms);
@@ -171,7 +174,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
- ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
+ ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
+ "last_write_time");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
permissions(dir, old_perms);
@@ -196,7 +200,8 @@ TEST_CASE(error_reporting) {
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
- ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
+ ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied,
+ "last_write_time");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
permissions(dir, old_perms);
diff --git a/libcxx/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp b/libcxx/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
index 1699a414af1..830f4a9436d 100644
--- a/libcxx/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
+++ b/libcxx/test/std/experimental/filesystem/class.rec.dir.itr/rec.dir.itr.members/increment.pass.cpp
@@ -25,6 +25,8 @@
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"
+#include <iostream>
+
using namespace fs;
TEST_SUITE(recursive_directory_iterator_increment_tests)
@@ -290,18 +292,15 @@ TEST_CASE(test_PR35078)
}
{
bool SeenNestedFile = false;
- recursive_directory_iterator it = SetupState(true, SeenNestedFile);
+ recursive_directory_iterator it = SetupState(false, SeenNestedFile);
TEST_REQUIRE(it != endIt);
TEST_REQUIRE(*it == nestedDir);
- ec = GetTestEC();
- it.increment(ec);
- TEST_CHECK(!ec);
- if (SeenNestedFile) {
- TEST_CHECK(it == endIt);
- } else {
- TEST_REQUIRE(it != endIt);
- TEST_CHECK(*it == nestedFile);
- }
+
+ ExceptionChecker Checker(std::errc::permission_denied,
+ "recursive_directory_iterator::operator++()",
+ format_string("attempting recursion into \"%s\"",
+ nestedDir.native()));
+ TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ++it);
}
}
diff --git a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp
index 3c2543ab942..5f023743a43 100644
--- a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp
+++ b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.copy_file/copy_file.pass.cpp
@@ -64,7 +64,7 @@ TEST_CASE(test_error_reporting) {
TEST_CHECK(fs::copy_file(file, file, copy_options::overwrite_existing,
ec) == false);
TEST_CHECK(ErrorIs(ec, std::errc::file_exists));
- ExceptionChecker Checker(file, file, std::errc::file_exists);
+ ExceptionChecker Checker(file, file, std::errc::file_exists, "copy_file");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing));
}
@@ -72,7 +72,7 @@ TEST_CASE(test_error_reporting) {
std::error_code ec;
TEST_CHECK(fs::copy_file(file, file2, ec) == false);
TEST_CHECK(ErrorIs(ec, std::errc::file_exists));
- ExceptionChecker Checker(file, file, std::errc::file_exists);
+ ExceptionChecker Checker(file, file, std::errc::file_exists, "copy_file");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing));
}
diff --git a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp
index e2b2513ecdb..c79012d9e13 100644
--- a/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp
+++ b/libcxx/test/std/experimental/filesystem/fs.op.funcs/fs.op.file_size/file_size.pass.cpp
@@ -69,14 +69,15 @@ TEST_CASE(file_size_error_cases)
{StaticEnv::Dir, std::errc::is_a_directory},
{StaticEnv::SymlinkToDir, std::errc::is_a_directory},
{StaticEnv::BadSymlink, std::errc::no_such_file_or_directory},
- {StaticEnv::DNE, std::errc::no_such_file_or_directory}};
+ {StaticEnv::DNE, std::errc::no_such_file_or_directory},
+ {"", std::errc::no_such_file_or_directory}};
const uintmax_t expect = static_cast<uintmax_t>(-1);
for (auto& TC : TestCases) {
std::error_code ec = GetTestEC();
TEST_CHECK(file_size(TC.p, ec) == expect);
TEST_CHECK(ErrorIs(ec, TC.expected_err));
- ExceptionChecker Checker(TC.p, TC.expected_err);
+ ExceptionChecker Checker(TC.p, TC.expected_err, "file_size");
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, file_size(TC.p));
}
}
diff --git a/libcxx/test/support/filesystem_test_helper.hpp b/libcxx/test/support/filesystem_test_helper.hpp
index de06868c014..f027928700b 100644
--- a/libcxx/test/support/filesystem_test_helper.hpp
+++ b/libcxx/test/support/filesystem_test_helper.hpp
@@ -9,8 +9,11 @@
#include <random>
#include <chrono>
#include <vector>
+#include <regex>
+#include "test_macros.h"
#include "rapid-cxx-test.hpp"
+#include "format_string.hpp"
// static test helpers
@@ -442,25 +445,77 @@ inline bool PathEq(fs::path const& LHS, fs::path const& RHS) {
}
struct ExceptionChecker {
- std::vector<std::errc> expected_err_list;
+ std::errc expected_err;
fs::path expected_path1;
fs::path expected_path2;
+ unsigned num_paths;
+ const char* func_name;
+ std::string opt_message;
+
+ explicit ExceptionChecker(std::errc first_err, const char* func_name,
+ std::string opt_msg = {})
+ : expected_err{first_err}, num_paths(0), func_name(func_name),
+ opt_message(opt_msg) {}
+ explicit ExceptionChecker(fs::path p, std::errc first_err,
+ const char* func_name, std::string opt_msg = {})
+ : expected_err(first_err), expected_path1(p), num_paths(1),
+ func_name(func_name), opt_message(opt_msg) {}
- template <class... ErrcT>
- explicit ExceptionChecker(fs::path p, std::errc first_err, ErrcT... rest_err)
- : expected_err_list({first_err, rest_err...}), expected_path1(p) {}
-
- template <class... ErrcT>
explicit ExceptionChecker(fs::path p1, fs::path p2, std::errc first_err,
- ErrcT... rest_err)
- : expected_err_list({first_err, rest_err...}), expected_path1(p1),
- expected_path2(p2) {}
+ const char* func_name, std::string opt_msg = {})
+ : expected_err(first_err), expected_path1(p1), expected_path2(p2),
+ num_paths(2), func_name(func_name), opt_message(opt_msg) {}
- void operator()(fs::filesystem_error const& Err) const {
- TEST_CHECK(ErrorIsImp(Err.code(), expected_err_list));
+ void operator()(fs::filesystem_error const& Err) {
+ TEST_CHECK(ErrorIsImp(Err.code(), {expected_err}));
TEST_CHECK(Err.path1() == expected_path1);
TEST_CHECK(Err.path2() == expected_path2);
+ LIBCPP_ONLY(check_libcxx_string(Err));
+ }
+
+ void check_libcxx_string(fs::filesystem_error const& Err) {
+ std::string message = std::make_error_code(expected_err).message();
+
+ std::string additional_msg = "";
+ if (!opt_message.empty()) {
+ additional_msg = opt_message + ": ";
+ }
+ auto transform_path = [](const fs::path& p) {
+ if (p.native().empty())
+ return "\"\"";
+ return p.c_str();
+ };
+ std::string format = [&]() -> std::string {
+ switch (num_paths) {
+ case 0:
+ return format_string("filesystem error: in %s: %s%s", func_name,
+ additional_msg, message);
+ case 1:
+ return format_string("filesystem error: in %s: %s%s [%s]", func_name,
+ additional_msg, message,
+ transform_path(expected_path1));
+ case 2:
+ return format_string("filesystem error: in %s: %s%s [%s] [%s]",
+ func_name, additional_msg, message,
+ transform_path(expected_path1),
+ transform_path(expected_path2));
+ default:
+ TEST_CHECK(false && "unexpected case");
+ return "";
+ }
+ }();
+ TEST_CHECK(format == Err.what());
+ if (format != Err.what()) {
+ fprintf(stderr,
+ "filesystem_error::what() does not match expected output:\n");
+ fprintf(stderr, " expected: \"%s\"\n", format.c_str());
+ fprintf(stderr, " actual: \"%s\"\n\n", Err.what());
+ }
}
+
+ ExceptionChecker(ExceptionChecker const&) = delete;
+ ExceptionChecker& operator=(ExceptionChecker const&) = delete;
+
};
#endif /* FILESYSTEM_TEST_HELPER_HPP */
diff --git a/libcxx/test/support/format_string.hpp b/libcxx/test/support/format_string.hpp
new file mode 100644
index 00000000000..ea3116facb5
--- /dev/null
+++ b/libcxx/test/support/format_string.hpp
@@ -0,0 +1,68 @@
+#ifndef TEST_SUPPORT_FORMAT_STRING_HPP
+#define TEST_SUPPORT_FORMAT_STRING_HPP
+
+#include <cstdio>
+#include <string>
+#include <memory>
+#include <array>
+
+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& target;
+ bool active = true;
+ void clear() {
+ if (active)
+ va_end(target);
+ active = false;
+ }
+ ~GuardVAList() {
+ if (active)
+ va_end(target);
+ }
+ };
+ 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
OpenPOWER on IntegriCloud