Nick, Thanks for your suggestions! They gave me some additional keywords to search for more ideas. After trying your code I found that I could actually use _Generic directly in the C sources, no need for autoconf, e.g.: time_t _unused_t; #define TIME_T_FMT _Generic((_unused_t), long long int: "%lld", long int: "%ld", int: "%d") Or even a generic int type format specifier handler: #define _FTM(x) _Generic((x), long long int: "%lld", long int: "%ld", int: "%d", \ unsigned long long int: "%llu", unsigned long int: "%lu", unsigned int: "%u") And for the case when we can't rely on C11, I just found there's __builtin_types_compatible_p available since at least gcc 3.1.1 (2002) and cland 1.0: #define TIME_T_FMT (__builtin_types_compatible_p(time_t, long long int) ? "%lld" : \ __builtin_types_compatible_p(time_t, long int) ? "%ld" : "%d") On 23/10/20 15:03, Nick Bowler wrote: > On 2020-10-23, Nick Bowler <nbowler@xxxxxxxxxx> wrote: >> On 23/10/2020, Paul Eggert <eggert@xxxxxxxxxxx> wrote: >>> On 10/22/20 6:09 PM, Russell Shaw wrote: >>>> else if(sizeof(time_t) == sizeof(long int)) { >>> >>> This is not the right kind of test. You want to test whether time_t and >>> int >>> are >>> the same types, not whether they're the same size. To do that, you should >>> use >>> code like this: >>> >>> extern time_t foo; >>> extern long int foo; >>> >>> Of course this means you'll need to compile N programs rather than >>> one, but that's life in the big Autoconf city. >> >> To improve configure performance when N is more than one or two, >> you can use C11 _Generic and AC_COMPUTE_INT to pretty easily and >> quickly determine which type (out of a finite list of candidates) >> time_t or any other type is compatible with. >> >> But you'd need a fallback (probably by compiling one program >> like the one shown abovce for each type) to handle the case >> where _Generic is not supported by the implementation. >> >> Example (totally untested): >> >> AC_COMPUTE_INT([timetype], >> [_Generic((time_t)0, long long: 3, default: 0) >> + _Generic((time_t)0, long: 2, default: 0) >> + _Generic((time_t)0, int: 1, default: 0)], > > On review, it is obvious this list of types could be more succinctly > written with a single _Generic as they are all for sure different: > > _Generic((time_t)0, long long: 3, long: 2, int: 1, default: 0) > > But care must be taken if any of the generic cases are themselves > typedefs, (for example, if we wanted to determine whether POSIX > ssize_t is compatible with a list of types that includes ptrdiff_t), > as it is an error to include compatible types among the list of > cases: > > /* error if ptrdiff_t happens to be compatible with long long */ > _Generic((ssize_t)0, long long: 2, ptrdiff_t: 1, default: 0) > > Nesting avoids this problem better than adding as I did originally: > > _Generic((ssize_t)0, long long: 2, default: > _Generic((ssize_t)0, ptrdiff_t: 1, default: 0)) > > as only one "branch" can be matched. > >> [#include <time.h>], >> [... slow fallback computation goes here]) >> >> AS_CASE([$timetype], >> [3], [... action when time_t is compatible with long long], >> [2], [... action when time_t is compatible with long], >> [1], [... action when time_t is compatible with int], >> [... action when time_t's compatibility is undetermined]) > > Cheers, > Nick >