mirror of
https://github.com/fmtlib/fmt.git
synced 2025-01-05 20:41:06 +00:00
Reduce compiled code size
If the number of arguments is less than MAX_PACKED_ARGS, pass the arguments as a Value array rather than Arg array.
This commit is contained in:
parent
62c483c940
commit
8d2559bd96
92
format.h
92
format.h
@ -707,9 +707,8 @@ struct NonZero {
|
||||
enum { VALUE = N > 0 ? N : 1 };
|
||||
};
|
||||
|
||||
// A formatting argument. It is a POD type to allow storage in
|
||||
// internal::MemoryBuffer.
|
||||
struct Arg {
|
||||
// A formatting argument value.
|
||||
struct Value {
|
||||
template <typename Char>
|
||||
struct StringValue {
|
||||
const Char *value;
|
||||
@ -747,6 +746,11 @@ struct Arg {
|
||||
DOUBLE, LONG_DOUBLE, LAST_NUMERIC_TYPE = LONG_DOUBLE,
|
||||
CSTRING, STRING, WSTRING, POINTER, CUSTOM
|
||||
};
|
||||
};
|
||||
|
||||
// A formatting argument. It is a POD type to allow storage in
|
||||
// internal::MemoryBuffer.
|
||||
struct Arg : Value {
|
||||
Type type;
|
||||
};
|
||||
|
||||
@ -800,6 +804,12 @@ struct EnableIf {};
|
||||
template<class T>
|
||||
struct EnableIf<true, T> { typedef T type; };
|
||||
|
||||
template<bool B, class T, class F>
|
||||
struct Conditional { typedef T type; };
|
||||
|
||||
template<class T, class F>
|
||||
struct Conditional<false, T, F> { typedef F type; };
|
||||
|
||||
// A helper function to suppress bogus "conditional expression is constant"
|
||||
// warnings.
|
||||
inline bool check(bool value) { return value; }
|
||||
@ -1068,7 +1078,15 @@ class ArgList {
|
||||
// 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::Arg *args_;
|
||||
union {
|
||||
// If the number of arguments is less than MAX_PACKED_ARGS, the argument
|
||||
// values are stored in values_, otherwise they are stored in args_.
|
||||
// This is done to reduce compiled code size as storing larger objects
|
||||
// may require more code (at least on x86-64) even if the same amount of
|
||||
// data is actually copied to stack. It saves ~10% on the bloat test.
|
||||
const internal::Value *values_;
|
||||
const internal::Arg *args_;
|
||||
};
|
||||
|
||||
internal::Arg::Type type(unsigned index) const {
|
||||
unsigned shift = index * 4;
|
||||
@ -1082,6 +1100,10 @@ class ArgList {
|
||||
enum { MAX_PACKED_ARGS = 16 };
|
||||
|
||||
ArgList() : types_(0) {}
|
||||
|
||||
// TODO: MakeArgList(const Args &...)
|
||||
ArgList(ULongLong types, const internal::Value *values)
|
||||
: types_(types), values_(values) {}
|
||||
ArgList(ULongLong types, const internal::Arg *args)
|
||||
: types_(types), args_(args) {}
|
||||
|
||||
@ -1089,14 +1111,18 @@ class ArgList {
|
||||
internal::Arg operator[](unsigned index) const {
|
||||
using internal::Arg;
|
||||
Arg arg;
|
||||
bool use_values = type(MAX_PACKED_ARGS - 1) == Arg::NONE;
|
||||
if (index < MAX_PACKED_ARGS) {
|
||||
Arg::Type arg_type = type(index);
|
||||
internal::Value &val = arg;
|
||||
if (arg_type != Arg::NONE)
|
||||
arg = args_[index];
|
||||
val = use_values ? values_[index] : args_[index];
|
||||
arg.type = arg_type;
|
||||
return arg;
|
||||
}
|
||||
if (type(MAX_PACKED_ARGS - 1) == Arg::NONE) {
|
||||
if (use_values) {
|
||||
// The index is greater than the number of arguments that can be stored
|
||||
// in values, so return a "none" argument.
|
||||
arg.type = Arg::NONE;
|
||||
return arg;
|
||||
}
|
||||
@ -1112,6 +1138,12 @@ struct FormatSpec;
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <std::size_t NUM_ARGS>
|
||||
struct SelectValueType {
|
||||
typedef typename Conditional<
|
||||
(NUM_ARGS < ArgList::MAX_PACKED_ARGS), Value, Arg>::type Type;
|
||||
};
|
||||
|
||||
class FormatterBase {
|
||||
private:
|
||||
ArgList args_;
|
||||
@ -1463,23 +1495,25 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
|
||||
# define FMT_VARIADIC_VOID(func, arg_type) \
|
||||
template <typename... Args> \
|
||||
void func(arg_type arg1, const Args & ... args) { \
|
||||
const fmt::internal::Arg array[ \
|
||||
fmt::internal::NonZero<sizeof...(Args)>::VALUE] = { \
|
||||
fmt::internal::MakeValue<Char>(args)... \
|
||||
namespace internal = fmt::internal; \
|
||||
typedef typename internal::SelectValueType<sizeof...(Args)>::Type Value; \
|
||||
const Value array[ \
|
||||
internal::NonZero<sizeof...(Args)>::VALUE] = { \
|
||||
internal::MakeValue<Char>(args)... \
|
||||
}; \
|
||||
func(arg1, ArgList(fmt::internal::make_type(args...), array)); \
|
||||
func(arg1, ArgList(internal::make_type(args...), array)); \
|
||||
}
|
||||
|
||||
// Defines a variadic constructor.
|
||||
# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \
|
||||
template <typename... Args> \
|
||||
ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \
|
||||
using fmt::internal::MakeValue; \
|
||||
const fmt::internal::Arg array[ \
|
||||
fmt::internal::NonZero<sizeof...(Args)>::VALUE] = { \
|
||||
MakeValue<Char>(args)... \
|
||||
namespace internal = fmt::internal; \
|
||||
typedef typename internal::SelectValueType<sizeof...(Args)>::Type Value; \
|
||||
const Value array[internal::NonZero<sizeof...(Args)>::VALUE] = { \
|
||||
internal::MakeValue<Char>(args)... \
|
||||
}; \
|
||||
func(arg0, arg1, ArgList(fmt::internal::make_type(args...), array)); \
|
||||
func(arg0, arg1, ArgList(internal::make_type(args...), array)); \
|
||||
}
|
||||
|
||||
#else
|
||||
@ -1492,9 +1526,9 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
|
||||
# define FMT_WRAP1(func, arg_type, n) \
|
||||
template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \
|
||||
inline void func(arg_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \
|
||||
const fmt::internal::Arg args[] = {FMT_GEN(n, FMT_MAKE_REF)}; \
|
||||
const fmt::internal::Value values[] = {FMT_GEN(n, FMT_MAKE_REF)}; \
|
||||
func(arg1, fmt::ArgList( \
|
||||
fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), args)); \
|
||||
fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), values)); \
|
||||
}
|
||||
|
||||
// Emulates a variadic function returning void on a pre-C++11 compiler.
|
||||
@ -1509,9 +1543,9 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) {
|
||||
# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \
|
||||
template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \
|
||||
ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \
|
||||
const fmt::internal::Arg args[] = {FMT_GEN(n, FMT_MAKE_REF)}; \
|
||||
const fmt::internal::Value values[] = {FMT_GEN(n, FMT_MAKE_REF)}; \
|
||||
func(arg0, arg1, fmt::ArgList( \
|
||||
fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), args)); \
|
||||
fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), values)); \
|
||||
}
|
||||
|
||||
// Emulates a variadic constructor on a pre-C++11 compiler.
|
||||
@ -2619,6 +2653,11 @@ inline void set_types(Arg *array, const Args & ... args) {
|
||||
array[sizeof...(Args)].type = Arg::NONE;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void set_types(Value *, const Args & ...) {
|
||||
// Do nothing as types are passed separately from values.
|
||||
}
|
||||
|
||||
// Computes the argument array size by adding 1 to N, which is the number of
|
||||
// arguments, if N is zero, because array of zero size is invalid, or if N
|
||||
// is greater than ArgList::MAX_PACKED_ARGS to accommodate for an extra
|
||||
@ -2634,14 +2673,15 @@ struct ArgArraySize {
|
||||
template <typename... Args> \
|
||||
ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \
|
||||
const Args & ... args) { \
|
||||
using fmt::internal::Arg; \
|
||||
Arg array[fmt::internal::ArgArraySize<sizeof...(Args)>::VALUE] = { \
|
||||
fmt::internal::MakeValue<Char>(args)... \
|
||||
namespace internal = fmt::internal; \
|
||||
typedef typename internal::SelectValueType<sizeof...(Args)>::Type Value; \
|
||||
Value array[internal::ArgArraySize<sizeof...(Args)>::VALUE] = { \
|
||||
internal::MakeValue<Char>(args)... \
|
||||
}; \
|
||||
if (fmt::internal::check((sizeof...(Args) > fmt::ArgList::MAX_PACKED_ARGS))) \
|
||||
if (internal::check((sizeof...(Args) > fmt::ArgList::MAX_PACKED_ARGS))) \
|
||||
set_types(array, args...); \
|
||||
call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \
|
||||
fmt::ArgList(fmt::internal::make_type(args...), array)); \
|
||||
fmt::ArgList(internal::make_type(args...), array)); \
|
||||
}
|
||||
#else
|
||||
// Defines a wrapper for a function taking __VA_ARGS__ arguments
|
||||
@ -2650,9 +2690,9 @@ struct ArgArraySize {
|
||||
template <FMT_GEN(n, FMT_MAKE_TEMPLATE_ARG)> \
|
||||
inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \
|
||||
FMT_GEN(n, FMT_MAKE_ARG)) { \
|
||||
const fmt::internal::Arg args[] = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \
|
||||
const fmt::internal::Value values[] = {FMT_GEN(n, FMT_MAKE_REF_##Char)}; \
|
||||
call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \
|
||||
fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), args)); \
|
||||
fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), values)); \
|
||||
}
|
||||
|
||||
# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \
|
||||
|
@ -423,7 +423,7 @@ ARG_INFO(POINTER, const void *, pointer);
|
||||
ARG_INFO(CUSTOM, Arg::CustomValue, custom);
|
||||
|
||||
#define CHECK_ARG_INFO(Type, field, value) { \
|
||||
Arg arg = {}; \
|
||||
Arg arg = Arg(); \
|
||||
arg.field = value; \
|
||||
EXPECT_EQ(value, ArgInfo<Arg::Type>::get(arg)); \
|
||||
}
|
||||
@ -442,7 +442,7 @@ TEST(ArgTest, ArgInfo) {
|
||||
CHECK_ARG_INFO(WSTRING, wstring.value, WSTR);
|
||||
int p = 0;
|
||||
CHECK_ARG_INFO(POINTER, pointer, &p);
|
||||
Arg arg = {};
|
||||
Arg arg = Arg();
|
||||
arg.custom.value = &p;
|
||||
EXPECT_EQ(&p, ArgInfo<Arg::CUSTOM>::get(arg).value);
|
||||
}
|
||||
@ -844,17 +844,28 @@ TEST(UtilTest, IsEnumConvertibleToInt) {
|
||||
|
||||
template <typename T>
|
||||
bool check_enable_if(
|
||||
typename fmt::internal::EnableIf<sizeof(T) == sizeof(int), T>::type) {
|
||||
typename fmt::internal::EnableIf<sizeof(T) == sizeof(int), T>::type *) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool check_enable_if(
|
||||
typename fmt::internal::EnableIf<sizeof(T) != sizeof(int), T>::type) {
|
||||
typename fmt::internal::EnableIf<sizeof(T) != sizeof(int), T>::type *) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST(UtilTest, EnableIf) {
|
||||
EXPECT_TRUE(check_enable_if<int>(42));
|
||||
EXPECT_FALSE(check_enable_if<char>('a'));
|
||||
int i = 0;
|
||||
EXPECT_TRUE(check_enable_if<int>(&i));
|
||||
char c = 0;
|
||||
EXPECT_FALSE(check_enable_if<char>(&c));
|
||||
}
|
||||
|
||||
TEST(UtilTest, Conditional) {
|
||||
int i = 0;
|
||||
fmt::internal::Conditional<true, int, char>::type *pi = &i;
|
||||
(void)pi;
|
||||
char c = 0;
|
||||
fmt::internal::Conditional<false, int, char>::type *pc = &c;
|
||||
(void)pc;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user