Я смотрю на tgmath.h
и пытается понять, как именно он выбирает правильную функцию на основе размера входного значения.
Специальный соус, кажется, это __tg_promote
макрос, но чем глубже я копаю, тем глубже эта загадка. У кого-нибудь есть короткий ответ на что __tg_promote
на самом деле
В реализации Clang tgmath.h
похоже, что __tg_promote
на самом деле функция, а не макрос. Определение можно найти Вот.
typedef void _Argument_type_is_not_arithmetic;
static _Argument_type_is_not_arithmetic __tg_promote(...)
__attribute__((__unavailable__,__overloadable__));
static double _TG_ATTRSp __tg_promote(int);
static double _TG_ATTRSp __tg_promote(unsigned int);
static double _TG_ATTRSp __tg_promote(long);
static double _TG_ATTRSp __tg_promote(unsigned long);
static double _TG_ATTRSp __tg_promote(long long);
static double _TG_ATTRSp __tg_promote(unsigned long long);
static float _TG_ATTRSp __tg_promote(float);
static double _TG_ATTRSp __tg_promote(double);
static long double _TG_ATTRSp __tg_promote(long double);
static float _Complex _TG_ATTRSp __tg_promote(float _Complex);
static double _Complex _TG_ATTRSp __tg_promote(double _Complex);
static long double _Complex _TG_ATTRSp __tg_promote(long double _Complex);
Это функция с множественными перегрузками (не разрешена в C вообще) и без определения, что хорошо, потому что она никогда не вызывается! __tg_promote
используется только для определения типа, которому должен быть присвоен числовой тип. (Интегральные типы для double
; Типы с плавающей точкой для себя.) Это ясно, когда вы смотрите на следующие несколько макросов:
#define __tg_promote1(__x) (__typeof__(__tg_promote(__x)))
#define __tg_promote2(__x, __y) (__typeof__(__tg_promote(__x) + \
__tg_promote(__y)))
#define __tg_promote3(__x, __y, __z) (__typeof__(__tg_promote(__x) + \
__tg_promote(__y) + \
__tg_promote(__z)))
__tg_promote
функция не вызывается, потому что это происходит внутри конкретного компилятора __typeof__
макро. __tg_promote1
макрос просто расширяется до повышенный тип его аргумент, в скобках. __tg_promote2
расширяется до типа (снова заключенного в скобки), который получился бы, если бы были добавлены два значения повышенных типов его аргументов. Так, например, __tg_promote2(0.0f, 0)
было бы (double)
, так как добавление float
и double
(результат продвижения int
) дает double
, __tg_promote3
похож.
Остальная часть заголовка состоит из перегруженных определений функций, которые делегируются соответствующим обычным функциям C:
// atan2
static float
_TG_ATTRS
__tg_atan2(float __x, float __y) {return atan2f(__x, __y);}
static double
_TG_ATTRS
__tg_atan2(double __x, double __y) {return atan2(__x, __y);}
static long double
_TG_ATTRS
__tg_atan2(long double __x, long double __y) {return atan2l(__x, __y);}
Для того, чтобы иметь возможность позвонить, скажем, atan2(1.0f, 1)
мы должны быть в состоянии делегировать __tg_atan2(double, double)
, Это где __tg_promote2
приходит, чтобы определить, что, когда у нас есть один float
аргумент и один int
аргумент, оба должны быть преобразованы в double
:
#define atan2(__x, __y) __tg_atan2(__tg_promote2((__x), (__y))(__x), \
__tg_promote2((__x), (__y))(__y))
Так что в этом случае __tg_promote2((__x), (__y))
расширяется до (double)
и мы получаем __tg_atan2((double)(__x), (double)(__y))
что именно то, что мы хотим.