Cause compilation error in C89 if two types are not the same
Asked Answered
E

2

19

Using only the features of C89, given

typedef [unspecified token sequence] T1;
typedef [another unspecified token sequence] T2;

exhibit a language construct which will compile without error if and only if T1 and T2 are the same type (not just compatible). The limitation to C89 is because this is going into an autoconf probe.

EDIT: I need a solution which works even if T1 or T2 or both are incomplete types. Sorry for not mentioning this before.

SON OF EDIT: All three current answers only detect compatible types. This turns out to be much closer to "the same type" than I remembered, close enough for my current purposes, but out of curiosity I am still looking for an answer that detects the same type. Here are some pairs of types that are compatible but not the same:

typedef void (*T1)(void);
typedef void (*T2)();

typedef float T1[];
typedef float T2[12];

typedef enum { ONE, TWO, THREE } T1;
typedef /* implementation-defined integer type */ T2;
Ensphere answered 20/2, 2013 at 18:33 Comment(8)
I suppose a compiler warning is not good enough?Livia
@NikosC. If compiled with -Werror, a warning will become an error, so that is probably fine.Crepuscule
I'd prefer a hard error for operational reasons (not having to muck with CFLAGS in the autoconf probe) but a warning will do.Ensphere
see: https://mcmap.net/q/45076/-what-is-39-39-in-cSolorzano
@Solorzano There are several such tricks for failing the build if an expression (technically, an integer constant expression) evaluates to some fixed value, but as far as I know, none of them can be applied to test type equality in C89.Ensphere
It just occured to me that compatible types can be used interchangeably actually. I had the impression that "compatible" means something like int vs unsigned. But no. Those are actually not compatible. For all intents and purposes I can think of, compatible types can be treated as equal. So why is type compatibility not good enough in your case?Livia
@NikosC. If you're writing a library with headers that can be used from both C and C++, you have to worry about name-mangling mismatches in C++ library-users -- even if the library itself is C-only and properly extern "C"-tagged. Name mangling uses a stricter definition of 'the same type' than C89's 'compatible'; for instance, in your enum vs int example, T1 and T2 would mangle differently. I don't want to have to have a C++ compiler around for the library build just to detect this kind of type mismatch (it would be the only thing it was used for).Ensphere
@NikosC. That said, I may not need to probe any types for which the difference matters. It looks like enum vs. underlying integer type is the only scenario liable to come up in practice, and for the specific thing I'm doing, it shouldn't happen.Ensphere
L
7

I think you should be able to utilize the strict type checking of extern declarations:

typedef int T1;
typedef char T2;

extern T1 t1;    
T2 t1;

The above will not compile. Changing T2 to an int will allow the source to build correctly.

This will also not compile:

typedef int T1;
typedef unsigned int T2;

extern T1 t1;    
T2 t1;

Even though both types are int. Which I think is what you want.

However, this will not trigger an error:

typedef emum {Foo} T1;
typedef unsigned T2;

So it's not 100% waterproof. However, one has to keep in mind that there's nothing one can do with an enum that can't also be done with an unsigned. They have the same layout and can be used interchangeably. Effectively, they are the same type.

Livia answered 20/2, 2013 at 19:0 Comment(4)
Nice! Yes, this is the behavior I want. I'm going to check exactly what the standard says about this construct before I accept your answer, but I'm pretty sure you're right.Ensphere
On checking C99, this doesn't work if T1 or T2 or both are incomplete types. This is fixable by making both declarations of t1 be pointers to T1 and T2 respectively. Also, technically it only requires T1 and T2 to be compatible types. That turns out to be much closer to "the same type" than I remembered, and will probably do for my purposes, but I'm still curious whether strict type equality can be detected.Ensphere
@Zack I'm not sure what you mean. If the types aren't the same, I can't get this to compile. Can you give an example where it compiles while the types aren't the same?Livia
@Zack Never mind, I found a counter example by using an emum.Livia
D
2
T1 t1;
T2 *t2 = &t1;

This is a constraint violation if T1 and T2 are not the same.

Or,

T1 f();
T2 f() {}
Defrayal answered 20/2, 2013 at 20:24 Comment(4)
These also only detect compatible types, not the same type.Ensphere
Hmm. Shouldn't the latter require the same type, not just compatible?Defrayal
The function prototype return type approach seems to have the same results as the extern answer, including not catching enum vs unsigned as being different.Livia
I can't find any constraint (in C99; I don't have an electronic copy of C89) for function definitions which is more stringent than 6.7p4 "All declarations in the same scope that refer to the same object or function shall specify compatible types." And 6.7.5.3p15 says "For two function types to be compatible, both shall specify compatible return types. Moreover, [...]" (emphasis mine).Ensphere

© 2022 - 2024 — McMap. All rights reserved.