Pass as many types as possible in a single integer to reduce code bloat

This commit is contained in:
vitaut 2015-03-24 08:55:40 -07:00
parent 1addec97bc
commit cf04d98d06
4 changed files with 92 additions and 105 deletions

137
format.h
View File

@ -743,9 +743,7 @@ struct Value {
StringValue<wchar_t> wstring; StringValue<wchar_t> wstring;
CustomValue custom; CustomValue custom;
}; };
};
struct Arg : Value {
enum Type { enum Type {
NONE, NONE,
// Integer types should go first, // Integer types should go first,
@ -757,6 +755,8 @@ struct Arg : Value {
Type type; Type type;
}; };
typedef struct Value Arg; // TODO: remove
template <typename T = void> template <typename T = void>
struct None {}; struct None {};
@ -1058,58 +1058,52 @@ class RuntimeError : public std::runtime_error {
template <typename Char> template <typename Char>
class ArgFormatter; class ArgFormatter;
/**
A type list utility class storing packed type data.
*/
class TypeList {
private:
const uint64_t *types_;
unsigned count_;
public:
TypeList(const uint64_t *types, unsigned count)
: types_(types), count_(count) {}
/**
Returns the argument type at specified index.
*/
Arg::Type operator[](unsigned index) const {
if (index >= count_)
return Arg::NONE;
unsigned shift = (index & 0xf) << 2; // (index % 16) * 4
uint64_t mask = 0xf;
uint64_t type = (types_[index >> 4] >> shift) & mask;
return static_cast<Arg::Type>(type);
}
};
} // namespace internal } // namespace internal
/** /** An argument list. */
An argument list.
*/
class ArgList { class ArgList {
private: private:
internal::TypeList types_; // To reduce compiled code size per formatting function call, types of first
// MAX_PACKED_ARGS arguments are passed in the types_ field.
uint64_t types_;
const internal::Value *values_; const internal::Value *values_;
internal::Arg::Type type(unsigned index) const {
unsigned shift = index * 4;
uint64_t mask = 0xf;
return static_cast<internal::Arg::Type>(
(types_ & (mask << shift)) >> shift);
}
public: public:
ArgList() : types_(NULL, 0) {} // Maximum number of arguments with packed types.
ArgList(const internal::TypeList &types, const internal::Value *values) enum { MAX_PACKED_ARGS = 16 };
ArgList() : types_(0) {}
ArgList(ULongLong types, const internal::Value *values)
: types_(types), values_(values) {} : types_(types), values_(values) {}
/** /** Returns the argument at specified index. */
Returns the argument at specified index.
*/
internal::Arg operator[](unsigned index) const { internal::Arg operator[](unsigned index) const {
using internal::Arg; using internal::Arg;
Arg arg; Arg arg;
Arg::Type type = types_[index]; if (index >= MAX_PACKED_ARGS) {
arg.type = type; if (type(MAX_PACKED_ARGS - 1) == Arg::NONE) {
if (type != Arg::NONE) { arg.type = Arg::NONE;
return arg;
}
for (unsigned i = MAX_PACKED_ARGS; i <= index; ++i) {
if (values_[i].type == Arg::NONE)
return values_[i];
}
return values_[index];
}
Arg::Type arg_type = type(index);
if (arg_type != Arg::NONE) {
internal::Value &value = arg; internal::Value &value = arg;
value = values_[index]; value = values_[index];
} }
arg.type = arg_type;
return arg; return arg;
} }
}; };
@ -1426,18 +1420,15 @@ inline StrFormatSpec<wchar_t> pad(
# define FMT_GEN15(f) FMT_GEN14(f), f(14) # define FMT_GEN15(f) FMT_GEN14(f), f(14)
namespace internal { namespace internal {
inline void make_type(uint64_t* out, unsigned index) {} inline uint64_t make_type() { return 0; }
template <typename T> template <typename T>
inline void make_type(uint64_t* out, unsigned index, const T &arg) { inline uint64_t make_type(const T &arg) { return MakeValue<char>::type(arg); }
out[index >> 4] |= MakeValue<char>::type(arg) << ((index & 0xf) << 2);
}
#if FMT_USE_VARIADIC_TEMPLATES #if FMT_USE_VARIADIC_TEMPLATES
template <typename Arg, typename... Args> template <typename Arg, typename... Args>
inline void make_type(uint64_t* out, unsigned index, const Arg &first, const Args & ... tail) { inline uint64_t make_type(const Arg &first, const Args & ... tail) {
make_type(out, index, first); return make_type(first) | (make_type(tail...) << 4);
make_type(out, index + 1, tail...);
} }
#else #else
@ -1447,13 +1438,13 @@ struct ArgType {
ArgType() : type(0) {} ArgType() : type(0) {}
template <typename T> template <typename T>
ArgType(const T &arg) : type(0) { make_type(&type, 0, arg); } ArgType(const T &arg) : type(make_type(arg)) {}
}; };
# define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType() # define FMT_ARG_TYPE_DEFAULT(n) ArgType t##n = ArgType()
inline void make_type(uint64_t* out, unsigned /*index*/, FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) { inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
*out = t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) | return t0.type | (t1.type << 4) | (t2.type << 8) | (t3.type << 12) |
(t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) | (t4.type << 16) | (t5.type << 20) | (t6.type << 24) | (t7.type << 28) |
(t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) | (t8.type << 32) | (t9.type << 36) | (t10.type << 40) | (t11.type << 44) |
(t12.type << 48) | (t13.type << 52) | (t14.type << 56); (t12.type << 48) | (t13.type << 52) | (t14.type << 56);
@ -1476,9 +1467,7 @@ inline void make_type(uint64_t* out, unsigned /*index*/, FMT_GEN15(FMT_ARG_TYPE_
fmt::internal::NonZero<sizeof...(Args)>::VALUE] = { \ fmt::internal::NonZero<sizeof...(Args)>::VALUE] = { \
fmt::internal::MakeValue<Char>(args)... \ fmt::internal::MakeValue<Char>(args)... \
}; \ }; \
uint64_t types[fmt::internal::NonZero<((sizeof...(Args)) + 15) / 16>::VALUE] = { }; \ func(arg1, ArgList(fmt::internal::make_type(args...), values)); \
fmt::internal::make_type(types, 0, args...); \
func(arg1, ArgList(fmt::internal::TypeList(types, sizeof...(Args)), values)); \
} }
// Defines a variadic constructor. // Defines a variadic constructor.
@ -1490,16 +1479,13 @@ inline void make_type(uint64_t* out, unsigned /*index*/, FMT_GEN15(FMT_ARG_TYPE_
fmt::internal::NonZero<sizeof...(Args)>::VALUE] = { \ fmt::internal::NonZero<sizeof...(Args)>::VALUE] = { \
MakeValue<Char>(args)... \ MakeValue<Char>(args)... \
}; \ }; \
uint64_t types[fmt::internal::NonZero<((sizeof...(Args)) + 15) / 16>::VALUE] = {}; \ func(arg0, arg1, ArgList(fmt::internal::make_type(args...), values)); \
fmt::internal::make_type(types, 0, args...); \
func(arg0, arg1, ArgList(fmt::internal::TypeList(types, sizeof...(Args)), values)); \
} }
#else #else
# define FMT_MAKE_REF(n) fmt::internal::MakeValue<Char>(v##n) # define FMT_MAKE_REF(n) fmt::internal::MakeValue<Char>(v##n)
# define FMT_MAKE_REF2(n) v##n # define FMT_MAKE_REF2(n) v##n
# define FMT_MAKE_ZERO(n) 0
// Defines a wrapper for a function taking one argument of type arg_type // Defines a wrapper for a function taking one argument of type arg_type
// and n additional arguments of arbitrary types. // and n additional arguments of arbitrary types.
@ -1507,10 +1493,8 @@ inline void make_type(uint64_t* out, unsigned /*index*/, FMT_GEN15(FMT_ARG_TYPE_
template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \ template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \
inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \
const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \
uint64_t types = 0; \
fmt::internal::make_type(&types, 0, FMT_GEN(n, FMT_MAKE_REF2)); \
func(arg1, fmt::ArgList( \ func(arg1, fmt::ArgList( \
fmt::internal::TypeList(&types, n), vals)); \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \
} }
// Emulates a variadic function returning void on a pre-C++11 compiler. // Emulates a variadic function returning void on a pre-C++11 compiler.
@ -1526,10 +1510,8 @@ inline void make_type(uint64_t* out, unsigned /*index*/, FMT_GEN15(FMT_ARG_TYPE_
template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \ template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \
ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \
const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF)}; \
uint64_t types = 0; \
fmt::internal::make_type(&types, 0, FMT_GEN(n, FMT_MAKE_REF2)); \
func(arg0, arg1, fmt::ArgList( \ func(arg0, arg1, fmt::ArgList( \
fmt::internal::TypeList(&types, n), vals)); \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \
} }
// Emulates a variadic constructor on a pre-C++11 compiler. // Emulates a variadic constructor on a pre-C++11 compiler.
@ -2622,18 +2604,33 @@ inline void format_decimal(char *&buffer, T value) {
#define FMT_GET_ARG_NAME(type, index) arg##index #define FMT_GET_ARG_NAME(type, index) arg##index
#if FMT_USE_VARIADIC_TEMPLATES #if FMT_USE_VARIADIC_TEMPLATES
namespace fmt {
namespace internal {
inline void set_types(Value *) {}
template <typename T, typename... Args>
inline void set_types(Value *values, const T &arg, const Args & ... tail) {
values->type = static_cast<Arg::Type>(MakeValue<T>::type(arg));
set_types(values + 1, tail...);
}
}
}
# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ # define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \
template <typename... Args> \ template <typename... Args> \
ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \
const Args & ... args) { \ const Args & ... args) { \
using fmt::internal::Value; \ using fmt::internal::Arg; \
const Value values[fmt::internal::NonZero<sizeof...(Args)>::VALUE] = { \ Arg array[sizeof...(Args) + 1] = { \
fmt::internal::MakeValue<Char>(args)... \ fmt::internal::MakeValue<Char>(args)... \
}; \ }; \
uint64_t types[fmt::internal::NonZero<((sizeof...(Args)) + 15) / 16>::VALUE] = {}; \ if (sizeof...(Args) > fmt::ArgList::MAX_PACKED_ARGS) { \
fmt::internal::make_type(types, 0, args...); \ set_types(array, args...); \
call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ array[sizeof...(Args)].type = Arg::NONE; \
fmt::internal::TypeList(types, sizeof...(Args)), values)); \ } \
call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \
fmt::ArgList(fmt::internal::make_type(args...), array)); \
} }
#else #else
// Defines a wrapper for a function taking __VA_ARGS__ arguments // Defines a wrapper for a function taking __VA_ARGS__ arguments
@ -2643,10 +2640,8 @@ inline void format_decimal(char *&buffer, T value) {
inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \
FMT_GEN(n, FMT_MAKE_ARG)) { \ FMT_GEN(n, FMT_MAKE_ARG)) { \
const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \ const fmt::internal::Value vals[] = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \
uint64_t types = 0; \
fmt::internal::make_type(&types, 0, FMT_GEN(n, FMT_MAKE_REF2)); \
call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \
fmt::internal::TypeList(&types, n), vals)); \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), vals)); \
} }
# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ # define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \

View File

@ -551,6 +551,32 @@ TEST(FormatterTest, ArgErrors) {
EXPECT_THROW_MSG(format(format_str), FormatError, "number is too big"); EXPECT_THROW_MSG(format(format_str), FormatError, "number is too big");
} }
#if FMT_USE_VARIADIC_TEMPLATES
template <int N>
struct TestFormat {
template <typename... Args>
static std::string format(fmt::StringRef format_str, const Args & ... args) {
return TestFormat<N - 1>::format(format_str, N - 1, args...);
}
};
template <>
struct TestFormat<0> {
template <typename... Args>
static std::string format(fmt::StringRef format_str, const Args & ... args) {
return fmt::format(format_str, args...);
}
};
TEST(FormatterTest, ManyArgs) {
EXPECT_EQ("19", TestFormat<20>::format("{19}"));
EXPECT_THROW_MSG(TestFormat<20>::format("{20}"),
FormatError, "argument index out of range");
EXPECT_THROW_MSG(TestFormat<21>::format("{21}"),
FormatError, "argument index out of range");
}
#endif
TEST(FormatterTest, AutoArgIndex) { TEST(FormatterTest, AutoArgIndex) {
EXPECT_EQ("abc", format("{}{}{}", 'a', 'b', 'c')); EXPECT_EQ("abc", format("{}{}{}", 'a', 'b', 'c'));
EXPECT_THROW_MSG(format("{0}{}", 'a', 'b'), EXPECT_THROW_MSG(format("{0}{}", 'a', 'b'),

View File

@ -436,26 +436,6 @@ TEST(PrintfTest, Enum) {
EXPECT_PRINTF("42", "%d", A); EXPECT_PRINTF("42", "%d", A);
} }
#if FMT_USE_VARIADIC_TEMPLATES
TEST(PrintfTest, ManyArgs) {
EXPECT_EQ("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 "
"21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 "
"41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 "
"61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 "
"81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99",
fmt::sprintf("%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d "
"%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60,
61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99));
}
#endif
#if FMT_USE_FILE_DESCRIPTORS #if FMT_USE_FILE_DESCRIPTORS
TEST(PrintfTest, Examples) { TEST(PrintfTest, Examples) {
const char *weekday = "Thursday"; const char *weekday = "Thursday";

View File

@ -567,20 +567,6 @@ TEST(ArgTest, MakeArg) {
EXPECT_EQ("test", w.str()); EXPECT_EQ("test", w.str());
} }
TEST(UtilTest, TypeList) {
uint64_t types[] = {0};
int a;
char* b;
double c;
fmt::internal::make_type(types, 0, a, b, c);
fmt::internal::TypeList typeList(types, 3);
EXPECT_EQ(typeList[0], Arg::INT);
EXPECT_EQ(typeList[1], Arg::CSTRING);
EXPECT_EQ(typeList[2], Arg::DOUBLE);
EXPECT_EQ(typeList[3], Arg::NONE);
}
TEST(UtilTest, ArgList) { TEST(UtilTest, ArgList) {
fmt::ArgList args; fmt::ArgList args;
EXPECT_EQ(Arg::NONE, args[1].type); EXPECT_EQ(Arg::NONE, args[1].type);