Add BasicCStringRef to represent a null-termated string (#100)

and use it instead of BasicStringRef in cases that assume that the
string is null-terminated such as POSIX function and format string
parser.
This commit is contained in:
vitaut 2015-06-26 07:43:54 -07:00
parent 88c7c20102
commit 438bd9b0e6
7 changed files with 141 additions and 83 deletions

View File

@ -527,7 +527,7 @@ class PrintfArgFormatter :
} // namespace fmt
FMT_FUNC void fmt::SystemError::init(
int err_code, StringRef format_str, ArgList args) {
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_system_error(w, err_code, format(format_str, args));
@ -927,7 +927,7 @@ unsigned fmt::internal::PrintfFormatter<Char>::parse_header(
template <typename Char>
void fmt::internal::PrintfFormatter<Char>::format(
BasicWriter<Char> &writer, BasicStringRef<Char> format_str,
BasicWriter<Char> &writer, BasicCStringRef<Char> format_str,
const ArgList &args) {
const Char *start = format_str.c_str();
set_args(args);
@ -1198,7 +1198,7 @@ const Char *fmt::BasicFormatter<Char>::format(
template <typename Char>
void fmt::BasicFormatter<Char>::format(
BasicStringRef<Char> format_str, const ArgList &args) {
BasicCStringRef<Char> format_str, const ArgList &args) {
const Char *s = start_ = format_str.c_str();
set_args(args);
while (*s) {
@ -1230,23 +1230,23 @@ FMT_FUNC void fmt::report_windows_error(
}
#endif
FMT_FUNC void fmt::print(std::FILE *f, StringRef format_str, ArgList args) {
FMT_FUNC void fmt::print(std::FILE *f, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void fmt::print(StringRef format_str, ArgList args) {
FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void fmt::print(std::ostream &os, StringRef format_str, ArgList args) {
FMT_FUNC void fmt::print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
os.write(w.data(), w.size());
}
FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) {
FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = '0' + static_cast<char>(c);
std::fputs(escape, stdout);
@ -1254,7 +1254,7 @@ FMT_FUNC void fmt::print_colored(Color c, StringRef format, ArgList args) {
std::fputs(RESET_COLOR, stdout);
}
FMT_FUNC int fmt::fprintf(std::FILE *f, StringRef format, ArgList args) {
FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
@ -1273,10 +1273,10 @@ template const char *fmt::BasicFormatter<char>::format(
const char *&format_str, const fmt::internal::Arg &arg);
template void fmt::BasicFormatter<char>::format(
BasicStringRef<char> format, const ArgList &args);
CStringRef format, const ArgList &args);
template void fmt::internal::PrintfFormatter<char>::format(
BasicWriter<char> &writer, BasicStringRef<char> format, const ArgList &args);
BasicWriter<char> &writer, CStringRef format, const ArgList &args);
template int fmt::internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
@ -1294,10 +1294,10 @@ template const wchar_t *fmt::BasicFormatter<wchar_t>::format(
const wchar_t *&format_str, const fmt::internal::Arg &arg);
template void fmt::BasicFormatter<wchar_t>::format(
BasicStringRef<wchar_t> format, const ArgList &args);
BasicCStringRef<wchar_t> format, const ArgList &args);
template void fmt::internal::PrintfFormatter<wchar_t>::format(
BasicWriter<wchar_t> &writer, BasicStringRef<wchar_t> format,
BasicWriter<wchar_t> &writer, WCStringRef format,
const ArgList &args);
template int fmt::internal::CharTraits<wchar_t>::format_float(

141
format.h
View File

@ -206,8 +206,7 @@ void format(BasicFormatter<Char> &f, const Char *&format_str, const T &value);
/**
\rst
A string reference. It can be constructed from a C string or
``std::string``.
A string reference. It can be constructed from a C string or ``std::string``.
You can use one of the following typedefs for common character types:
@ -236,9 +235,7 @@ class BasicStringRef {
std::size_t size_;
public:
/**
Constructs a string reference object from a C string and a size.
*/
/** Constructs a string reference object from a C string and a size. */
BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {}
/**
@ -264,17 +261,13 @@ class BasicStringRef {
\endrst
*/
std::basic_string<Char> to_string() const {
return std::basic_string<Char>(data_, size());
return std::basic_string<Char>(data_, size_);
}
/**
Returns the pointer to a C string.
*/
const Char *c_str() const { return data_; }
/** Returns the pointer to a C string. */
const Char *data() const { return data_; }
/**
Returns the string size.
*/
/** Returns the string size. */
std::size_t size() const { return size_; }
friend bool operator==(BasicStringRef lhs, BasicStringRef rhs) {
@ -284,19 +277,69 @@ class BasicStringRef {
return lhs.data_ != rhs.data_;
}
friend bool operator<(BasicStringRef lhs, BasicStringRef rhs) {
return std::lexicographical_compare(lhs.data_, lhs.data_ + lhs.size_, rhs.data_, rhs.data_ + rhs.size_);
return std::lexicographical_compare(
lhs.data_, lhs.data_ + lhs.size_, rhs.data_, rhs.data_ + rhs.size_);
}
};
typedef BasicStringRef<char> StringRef;
typedef BasicStringRef<wchar_t> WStringRef;
/**
\rst
A reference to a null terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following typedefs for common character types:
+-------------+--------------------------+
| Type | Definition |
+=============+==========================+
| CStringRef | BasicCStringRef<char> |
+-------------+--------------------------+
| WCStringRef | BasicCStringRef<wchar_t> |
+-------------+--------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(CStringRef format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char>
class BasicCStringRef {
private:
const Char *data_;
public:
/** Constructs a string reference object from a C string. */
BasicCStringRef(const Char *s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
BasicCStringRef(const std::basic_string<Char> &s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char *c_str() const { return data_; }
};
typedef BasicCStringRef<char> CStringRef;
typedef BasicCStringRef<wchar_t> WCStringRef;
/**
A formatting error such as invalid format string.
*/
class FormatError : public std::runtime_error {
public:
explicit FormatError(StringRef message)
explicit FormatError(CStringRef message)
: std::runtime_error(message.c_str()) {}
};
@ -874,12 +917,12 @@ class MakeValue : public Arg {
MakeValue(typename WCharHelper<WStringRef, Char>::Unsupported);
void set_string(StringRef str) {
string.value = str.c_str();
string.value = str.data();
string.size = str.size();
}
void set_string(WStringRef str) {
wstring.value = str.c_str();
wstring.value = str.data();
wstring.size = str.size();
}
@ -895,10 +938,13 @@ class MakeValue : public Arg {
public:
MakeValue() {}
#define FMT_MAKE_VALUE(Type, field, TYPE) \
MakeValue(Type value) { field = value; } \
#define FMT_MAKE_VALUE_(Type, field, TYPE, rhs) \
MakeValue(Type value) { field = rhs; } \
static uint64_t type(Type) { return Arg::TYPE; }
#define FMT_MAKE_VALUE(Type, field, TYPE) \
FMT_MAKE_VALUE_(Type, field, TYPE, value)
FMT_MAKE_VALUE(bool, int_value, BOOL)
FMT_MAKE_VALUE(short, int_value, INT)
FMT_MAKE_VALUE(unsigned short, uint_value, UINT)
@ -952,6 +998,7 @@ class MakeValue : public Arg {
FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING)
FMT_MAKE_STR_VALUE(const std::string &, STRING)
FMT_MAKE_STR_VALUE(StringRef, STRING)
FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str())
#define FMT_MAKE_WSTR_VALUE(Type, TYPE) \
MakeValue(typename WCharHelper<Type, Char>::Supported value) { \
@ -1270,7 +1317,7 @@ class PrintfFormatter : private FormatterBase {
public:
void format(BasicWriter<Char> &writer,
BasicStringRef<Char> format_str, const ArgList &args);
BasicCStringRef<Char> format_str, const ArgList &args);
};
} // namespace internal
@ -1301,7 +1348,7 @@ class BasicFormatter : private internal::FormatterBase {
BasicWriter<Char> &writer() { return writer_; }
void format(BasicStringRef<Char> format_str, const ArgList &args);
void format(BasicCStringRef<Char> format_str, const ArgList &args);
const Char *format(const Char *&format_str, const internal::Arg &arg);
};
@ -1729,7 +1776,7 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
*/
class SystemError : public internal::RuntimeError {
private:
void init(int err_code, StringRef format_str, ArgList args);
void init(int err_code, CStringRef format_str, ArgList args);
protected:
int error_code_;
@ -1764,10 +1811,10 @@ class SystemError : public internal::RuntimeError {
throw fmt::SystemError(errno, "cannot open file '{}'", filename);
\endrst
*/
SystemError(int error_code, StringRef message) {
SystemError(int error_code, CStringRef message) {
init(error_code, message, ArgList());
}
FMT_VARIADIC_CTOR(SystemError, init, int, StringRef)
FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef)
int error_code() const { return error_code_; }
};
@ -1942,10 +1989,10 @@ class BasicWriter {
See also :ref:`syntax`.
\endrst
*/
void write(BasicStringRef<Char> format, ArgList args) {
void write(BasicCStringRef<Char> format, ArgList args) {
BasicFormatter<Char>(*this).format(format, args);
}
FMT_VARIADIC_VOID(write, BasicStringRef<Char>)
FMT_VARIADIC_VOID(write, BasicCStringRef<Char>)
BasicWriter &operator<<(int value) {
return *this << IntFormatSpec<int>(value);
@ -2008,14 +2055,14 @@ class BasicWriter {
\endrst
*/
BasicWriter &operator<<(fmt::BasicStringRef<Char> value) {
const Char *str = value.c_str();
const Char *str = value.data();
buffer_.append(str, str + value.size());
return *this;
}
BasicWriter &operator<<(
typename internal::WCharHelper<StringRef, Char>::Supported value) {
const char *str = value.c_str();
const char *str = value.data();
buffer_.append(str, str + value.size());
return *this;
}
@ -2561,7 +2608,7 @@ enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE };
Example:
PrintColored(fmt::RED, "Elapsed time: {0:.2f} seconds") << 1.23;
*/
void print_colored(Color c, StringRef format, ArgList args);
void print_colored(Color c, CStringRef format, ArgList args);
/**
\rst
@ -2572,13 +2619,13 @@ void print_colored(Color c, StringRef format, ArgList args);
std::string message = format("The answer is {}", 42);
\endrst
*/
inline std::string format(StringRef format_str, ArgList args) {
inline std::string format(CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
return w.str();
}
inline std::wstring format(WStringRef format_str, ArgList args) {
inline std::wstring format(WCStringRef format_str, ArgList args) {
WMemoryWriter w;
w.write(format_str, args);
return w.str();
@ -2593,7 +2640,7 @@ inline std::wstring format(WStringRef format_str, ArgList args) {
print(stderr, "Don't {}!", "panic");
\endrst
*/
void print(std::FILE *f, StringRef format_str, ArgList args);
void print(std::FILE *f, CStringRef format_str, ArgList args);
/**
\rst
@ -2604,7 +2651,7 @@ void print(std::FILE *f, StringRef format_str, ArgList args);
print("Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
void print(StringRef format_str, ArgList args);
void print(CStringRef format_str, ArgList args);
/**
\rst
@ -2615,10 +2662,10 @@ void print(StringRef format_str, ArgList args);
print(cerr, "Don't {}!", "panic");
\endrst
*/
void print(std::ostream &os, StringRef format_str, ArgList args);
void print(std::ostream &os, CStringRef format_str, ArgList args);
template <typename Char>
void printf(BasicWriter<Char> &w, BasicStringRef<Char> format, ArgList args) {
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args) {
internal::PrintfFormatter<Char>().format(w, format, args);
}
@ -2631,7 +2678,7 @@ void printf(BasicWriter<Char> &w, BasicStringRef<Char> format, ArgList args) {
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline std::string sprintf(StringRef format, ArgList args) {
inline std::string sprintf(CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
return w.str();
@ -2646,7 +2693,7 @@ inline std::string sprintf(StringRef format, ArgList args) {
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
int fprintf(std::FILE *f, StringRef format, ArgList args);
int fprintf(std::FILE *f, CStringRef format, ArgList args);
/**
\rst
@ -2657,7 +2704,7 @@ int fprintf(std::FILE *f, StringRef format, ArgList args);
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline int printf(StringRef format, ArgList args) {
inline int printf(CStringRef format, ArgList args) {
return fprintf(stdout, format, args);
}
@ -2918,15 +2965,15 @@ void arg(WStringRef, const internal::NamedArg<Char>&) FMT_DELETED_OR_UNDEFINED;
#define FMT_CAPTURE_W(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_W_, __VA_ARGS__)
namespace fmt {
FMT_VARIADIC(std::string, format, StringRef)
FMT_VARIADIC_W(std::wstring, format, WStringRef)
FMT_VARIADIC(void, print, StringRef)
FMT_VARIADIC(void, print, std::FILE *, StringRef)
FMT_VARIADIC(void, print, std::ostream &, StringRef)
FMT_VARIADIC(void, print_colored, Color, StringRef)
FMT_VARIADIC(std::string, sprintf, StringRef)
FMT_VARIADIC(int, printf, StringRef)
FMT_VARIADIC(int, fprintf, std::FILE *, StringRef)
FMT_VARIADIC(std::string, format, CStringRef)
FMT_VARIADIC_W(std::wstring, format, WCStringRef)
FMT_VARIADIC(void, print, CStringRef)
FMT_VARIADIC(void, print, std::FILE *, CStringRef)
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
FMT_VARIADIC(void, print_colored, Color, CStringRef)
FMT_VARIADIC(std::string, sprintf, CStringRef)
FMT_VARIADIC(int, printf, CStringRef)
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
}
// Restore warnings.

View File

@ -83,7 +83,8 @@ fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT {
fmt::report_system_error(errno, "cannot close file");
}
fmt::BufferedFile::BufferedFile(fmt::StringRef filename, fmt::StringRef mode) {
fmt::BufferedFile::BufferedFile(
fmt::CStringRef filename, fmt::CStringRef mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
if (!file_)
throw SystemError(errno, "cannot open file {}", filename);
@ -108,7 +109,7 @@ int fmt::BufferedFile::fileno() const {
return fd;
}
fmt::File::File(fmt::StringRef path, int oflag) {
fmt::File::File(fmt::CStringRef path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;

10
posix.h
View File

@ -181,7 +181,7 @@ public:
#endif
// Opens a file.
BufferedFile(fmt::StringRef filename, fmt::StringRef mode);
BufferedFile(CStringRef filename, CStringRef mode);
// Closes the file.
void close();
@ -193,10 +193,10 @@ public:
// of MinGW that define fileno as a macro.
int (fileno)() const;
void print(fmt::StringRef format_str, const ArgList &args) {
void print(CStringRef format_str, const ArgList &args) {
fmt::print(file_, format_str, args);
}
FMT_VARIADIC(void, print, fmt::StringRef)
FMT_VARIADIC(void, print, CStringRef)
};
// A file. Closed file is represented by a File object with descriptor -1.
@ -224,7 +224,7 @@ class File {
File() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a File object representing this file.
File(fmt::StringRef path, int oflag);
File(CStringRef path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
@ -296,7 +296,7 @@ class File {
void close();
// Returns the file size.
fmt::LongLong size() const;
LongLong size() const;
// Attempts to read count bytes from the file into the specified buffer.
std::size_t read(void *buffer, std::size_t count);

View File

@ -66,6 +66,7 @@ using fmt::BasicWriter;
using fmt::format;
using fmt::FormatError;
using fmt::StringRef;
using fmt::CStringRef;
using fmt::MemoryWriter;
using fmt::WMemoryWriter;
using fmt::pad;
@ -136,6 +137,24 @@ struct WriteChecker {
EXPECT_PRED_FORMAT1(WriteChecker<wchar_t>(), value)
} // namespace
TEST(StringRefTest, Ctor) {
EXPECT_STREQ("abc", StringRef("abc").data());
EXPECT_EQ(3u, StringRef("abc").size());
EXPECT_STREQ("defg", StringRef(std::string("defg")).data());
EXPECT_EQ(4u, StringRef(std::string("defg")).size());
}
TEST(StringRefTest, ConvertToString) {
std::string s = StringRef("abc").to_string();
EXPECT_EQ("abc", s);
}
TEST(CStringRefTest, Ctor) {
EXPECT_STREQ("abc", CStringRef("abc").c_str());
EXPECT_STREQ("defg", CStringRef(std::string("defg")).c_str());
}
class TestString {
private:
std::string value_;
@ -583,7 +602,7 @@ TEST(FormatterTest, ArgErrors) {
template <int N>
struct TestFormat {
template <typename... Args>
static std::string format(fmt::StringRef format_str, const Args & ... args) {
static std::string format(fmt::CStringRef format_str, const Args & ... args) {
return TestFormat<N - 1>::format(format_str, N - 1, args...);
}
};
@ -591,7 +610,7 @@ struct TestFormat {
template <>
struct TestFormat<0> {
template <typename... Args>
static std::string format(fmt::StringRef format_str, const Args & ... args) {
static std::string format(fmt::CStringRef format_str, const Args & ... args) {
return fmt::format(format_str, args...);
}
};
@ -1372,6 +1391,10 @@ TEST(FormatterTest, FormatStringRef) {
EXPECT_EQ("test", format("{0}", StringRef("test")));
}
TEST(FormatterTest, FormatCStringRef) {
EXPECT_EQ("test", format("{0}", CStringRef("test")));
}
TEST(FormatterTest, FormatUsingIOStreams) {
EXPECT_EQ("a string", format("{0}", TestString("a string")));
std::string s = format("The date is {0}", Date(2012, 12, 9));
@ -1440,19 +1463,6 @@ TEST(FormatterTest, FormatExamples) {
}, error_code, "Cannot open file 'nonexistent'");
}
TEST(StringRefTest, Ctor) {
EXPECT_STREQ("abc", StringRef("abc").c_str());
EXPECT_EQ(3u, StringRef("abc").size());
EXPECT_STREQ("defg", StringRef(std::string("defg")).c_str());
EXPECT_EQ(4u, StringRef(std::string("defg")).size());
}
TEST(StringRefTest, ConvertToString) {
std::string s = StringRef("abc").to_string();
EXPECT_EQ("abc", s);
}
TEST(FormatterTest, Examples) {
EXPECT_EQ("First, thou shalt count to three",
format("First, thou shalt count to {0}", "three"));

View File

@ -207,7 +207,7 @@ int (test::fileno)(FILE *stream) {
# define EXPECT_EQ_POSIX(expected, actual)
#endif
void write_file(fmt::StringRef filename, fmt::StringRef content) {
void write_file(fmt::CStringRef filename, fmt::StringRef content) {
fmt::BufferedFile f(filename, "w");
f.print("{}", content);
}

View File

@ -64,7 +64,7 @@ File open_file() {
// Attempts to write a string to a file.
void write(File &f, fmt::StringRef s) {
std::size_t num_chars_left = s.size();
const char *ptr = s.c_str();
const char *ptr = s.data();
do {
std::streamsize count = f.write(ptr, num_chars_left);
ptr += count;