8

How do I assert that two types are equal in C? In C++, I would use std::is_same, but searching StackOverflow and elsewhere seems only to give results for C++ and C#. Is there no way to do this in C?


Note, this is not asking whether a variable has a certain type but rather whether two types are the same.

Zach Boyd
  • 419
  • 1
  • 5
  • 23
  • Given that there's no `decltype` in C, you'd be stuck comparing typedefs or using `typeof` extensions even if you had a way to do this test. – user2357112 Dec 18 '18 at 20:25
  • Possible duplicate of [How do I check if a variable is of a certain type (compare two types) in C?](https://stackoverflow.com/questions/6280055/how-do-i-check-if-a-variable-is-of-a-certain-type-compare-two-types-in-c) – ShadowRanger Dec 18 '18 at 20:26
  • @ShadowRanger: That's testing a variable against a type, not testing two types against each other. – user2357112 Dec 18 '18 at 20:26
  • So this is not part of standard C, I take it? What is the barrier to implementing such a functionality, especially when types are known at compile time? Or is it just a question of "philosophy" in C versus C++? – Zach Boyd Dec 18 '18 at 20:58
  • 2
    In what form are the types available? If we can put them into type declarations, then something like `typedef T0; typedef T1;` may complain if and only if the types are different. (Testing shows it complains even if the types are compatible, such as an incomplete array and a complete array, but I need to check the C standard further.) But this requires they be in a declarable form with the same identifier, such as `int x[3]`. If we just have `int [3]`, preprocessor expansion cannot form `int x[3]`. – Eric Postpischil Dec 18 '18 at 21:10

2 Answers2

6

How to assert two types are equal in c?

Use _Generic to get you at least mostly there with non-array types.

#define compare_types(T1, T2) _Generic((  (T1){0}  ), \
  T2: "Same", \
  default: "Different" \
)

#include <stdio.h>
#include <stdint.h>

int main() {
  // Same range
  printf("%ld %lld\n", LONG_MAX, LLONG_MAX);
  // Same size
  printf("%zu %zu\n", sizeof (long), sizeof (long long));
  // Yet different
  printf("%s\n", compare_types(long, long long));

  // int64_t is a long on my machine
  printf("%s\n", compare_types(long, int64_t));
  printf("%s\n", compare_types(long long, int64_t));
}

Output

9223372036854775807 9223372036854775807
8 8
Different
Same
Different

Improved

Further, a stronger compare employs a A vs B and B vs A test. The 2 tests are useful for the controlling expression of _Generic converts arrays to pointers of the first element losing some type information.

#define strong_helper(T1, T2) _Generic(( (T1){0} ), \
  T2: 1, \
  default: 0 \
)
#define compare_types_strong(T1, T2) (strong_helper(T1,T2) && strong_helper(T2,T1))

printf("%d\n", compare_types_strong(long, int64_t));
printf("%d\n", compare_types_strong(int [3], int *));

Output

1
0

Still troublesome for arrays and void

compare_types_strong(int [3], int [3]) returns 0 as _Generic converted the controlling expression int [3] to a pointer to the first element type (int *).

@PSkocik, in a deleted comment, points out this approach will not work for the incomplete object type void.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • @EricPostpischil My `compare_types(int *, int [3])` reports `"Different"` (gcc version 7.3.0 ). What compiler/version are you using? – chux - Reinstate Monica Dec 18 '18 at 21:12
  • Oops, my mistake. `int *, int [3]` reports “Different”. `int [3], int *` gets a compilation error. Apple LLVM 9.1.0, clang-902.0.39.2. – Eric Postpischil Dec 18 '18 at 21:15
  • @PSkocik As standard C does not have `__typeof` nor `typeof`, using that would make for a compiler specific solution OK if OP wants that, but so far post is not tagged as such. – chux - Reinstate Monica Dec 19 '18 at 01:05
  • I use GCC for compliation and Clang/LLVM for static analysis, so a compiler-specific solution would be fine, although a standard-based solution is of course preferred. – Zach Boyd Dec 19 '18 at 16:55
  • @ZachBoyd In the end, I suspect that either 1) a standard based solution as above that does not work well for array and `void` or 2) a compiler-specific solution is the answer. In any case, the question implies no compiler-specific solution allowance. Perhaps explaining _why_ this this ability is needed and how it is used will result in an even better solution that is universal and portable. – chux - Reinstate Monica Dec 19 '18 at 17:03
  • 1
    Part of the question is just curiosity, but the specific motivating case I was looking at was for a library I rely on where a "real number" type is just floating point double but is not guaranteed to stay that way and/or may be configurable. I wanted to put a check in my code that uses the assumption that the real number type is exactly double to call attention in case of future changes. So in this case, the array case is not needed, although it is of interest in general. – Zach Boyd Dec 19 '18 at 17:09
  • 1
    @ZachBoyd Thanks for that detail. It is a prudent use of something like `_Static_assert(compare_types_strong(double, lib_FP), "Unexpected lib_FP");` – chux - Reinstate Monica Dec 19 '18 at 17:15
3

Under gcc, you could do something like this:

#define same_type(a, b) \ 
    static_assert(__builtin_types_compatible_p(typeof(a), typeof(b)), "types do not match")

...

int a, b;
float c;
same_type(a,b);
same_type(a,c);
dbush
  • 205,898
  • 23
  • 218
  • 273
  • Thank you. Do you know why this is not part of standard C, at least in the case where the types are known at compile time? – Zach Boyd Dec 18 '18 at 20:57
  • 1
    No one found it of sufficient importance to push to add it to the standard, or it entails too much extra infrastructure to be accepted, @ZachBoyd. – Jonathan Leffler Dec 18 '18 at 21:02
  • 1
    That makes sense--I suppose that keeping C compilers easy to implement is a valid priority. – Zach Boyd Dec 18 '18 at 21:04
  • @ZachBoyd: There's no need to have an assertion to test condition which would generate a compilation squawk if not satisfied, and I don't think anybody on the Committee imagined that anyone should care about whether an implementation with both 64-bit `long` and 64-bit `long long` mapped `int64_t` to the former or the latter in cases where a compiler wouldn't squawk. – supercat Nov 02 '21 at 21:13