7
const Boo *constBoo;
Boo *nonConstBoo;

nonConstBoo = ((union {const Boo *_q; Boo *_nq;})constBoo)._nq;

Is the above construct valid in C11, or is it only GCC/clang extension that you can cast a pointer to an anonymous union in this fashion? If it is not valid, is there any other way to write an equivalent expression in valid C11 code?

The intention was to emulate C++ const_cast that would be C11 compatible and provide some rudimentary type safety. An explicit cast from const to non-const pointer will trigger a warning with -Wcast-qual option, which is undesirable.

Filip Navara
  • 4,818
  • 1
  • 26
  • 37
  • Any reason you don't want to just do the straight forward cast? – StoryTeller - Unslander Monica Jul 08 '18 at 11:17
  • It wouldn't compile with -Wcast-qual. This is basically an explicit way of writing const_cast from C++ in C without triggering a warning. Unfortunately some external libraries don't define function prototypes properly so it is unavoidable to do the cast, but at the same time it is reasonable to enable the warnings for the rest of the code. – Filip Navara Jul 08 '18 at 11:20
  • 2
    GCC will issue an error with `-pedantic-errors`: "error: ISO C forbids casts to union type [-Wpedantic]" Also suggest rewording: I read your question as asking about pointer-to-union. :) –  Jul 08 '18 at 11:20
  • Hmm, interesting, I guess I missed that warning. Let me rephrase the question :-) Did you also specify -std=c11? What GCC version? – Filip Navara Jul 08 '18 at 11:23
  • Unaccept please. Now that I had the standard before me, I see the types are in fact not compatible. It really is unavoidable to do a cast. – StoryTeller - Unslander Monica Jul 08 '18 at 11:37
  • @StoryTeller I liked the idea with compound literals though. That looked way more reasonable than the cast (and still compiled fine with the compilers I had at hand). – Filip Navara Jul 08 '18 at 11:40
  • Yeah, I liked it too. But the type system still gets in the way. – StoryTeller - Unslander Monica Jul 08 '18 at 11:41
  • 1
    @FilipNavara Yes, that's `-std=c11 -pedantic-errors` with GCC 8.1, but same results as far back as GCC 4.7, the first to recognise `-std=c11`. –  Jul 08 '18 at 11:48
  • @FilipNavara But... "The goal is to enable an explicit cast from const to non-const pointer that works with -Wcast-qual warnings." -- Duplicate of https://stackoverflow.com/questions/13249756/explicit-ignore-warning-from-wcast-qual-cast-discards-attribute-const ? –  Jul 08 '18 at 11:51
  • @hvd Intent is the same, but the proposed solutions are not applicable. The accepted one is GCC specific (already got that covered with the 'union' code above in a way that can be expressed as a macro). The second one with (uintptr_t) cast may work, but I find it troublesome because it couldn't enforce proper type compatibility. The last answer simply doesn't work. – Filip Navara Jul 08 '18 at 12:03
  • To expand on the type compatibility: #define CONST_CAST2(TOTYPE,FROMTYPE,X) ((__extension__(union {FROMTYPE _q; TOTYPE _nq;})(X))._nq) #define CONST_CAST(TYPE,X) CONST_CAST2 (TYPE, const TYPE, (X)) ...works also for non-pointers, but it is GCC specific again. – Filip Navara Jul 08 '18 at 12:06
  • @FilipNavara That's trivially fixable. Just do `(Boo*)(uintptr_t)(const Boo*){constBoo}`... –  Jul 08 '18 at 12:11
  • @hvd According to the quotations of C99 from https://stackoverflow.com/questions/34291377/converting-a-non-void-pointer-to-uintptr-t-and-vice-versa it seems that is a valid way to do it. Feel free to whip up an answer. :) – Filip Navara Jul 08 '18 at 12:15
  • There's nothing in that comment that answers any part of this question. You haven't asked what it is you actually want to know, so I don't think it's appropriate to post it as an answer to your question right now. I think you started off from the wrong direction. Can you edit your question to something like "How do I enforce type safety when suppressing a -Wcast-qual warning? When I follow the suggestions from [...], it would silently compile even if my `const Boo *` were actually a `Quz *`. I've come up with [...]. Is this valid? If not, is there some alternative?" –  Jul 08 '18 at 12:22
  • I'd just do the normal cast but locally disable warnings (`#pragma`). Warnings are compiler specific anyway. – melpomene Jul 08 '18 at 12:23
  • @hvd Well, right. That's possibly what I should have asked in the first place, but I was actually interested in the nuance of C11 language (which was the version that introduced the anonymous unions) for this specific solution. I think "ISO C forbids casts to union type" is an answer to that. My update to the question was poorly worded unfortunately. It should have been something akin to "If not, is there some trivial change that would make it C11 compliant?". That was the answer with compound literal, which unfortunately was deleted because of some inaccuracies... – Filip Navara Jul 08 '18 at 12:34

2 Answers2

4

Cast to a union is a GNU C extension. The C standard only defines casts among scalar types (i.e., integers, floats, and pointers; see 6.5.4p2). What you could do, however, is copy-create the union on the spot (instead of casting to it) and then take the appropriate member:

typedef struct Boo Boo;
const Boo *constBoo;
Boo *nonConstBoo;

int main()
{
    nonConstBoo = (union {const Boo *_q; Boo *_nq;}){._q=constBoo}._nq;
}

The above should work (in C, but not C++ where you're required to access only the last used member of a union) because qualified and non-qualified objects must have the same representation and alignment requirements and the same applies to pointers to qualified and unqualified versions of compatible types (6.2.5p28).

memcpy(&nonConstBoo,&constBoo,sizeof constBoo);

should work in either language.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • This use of compound literals instead of a cast was previously suggested by @StoryTeller, but the answer was since deleted because he found something in the standard that prevented it from being valid. It seems to compile with both GCC and clang though. – Filip Navara Jul 08 '18 at 12:37
  • The objects may need to have the same representation, but the pointers to them need not. It's' not unfeasible for a cast to not be value preserving, so the punning will not be equivalent to casting. Standard-wise, anyway. – StoryTeller - Unslander Monica Jul 08 '18 at 12:41
  • @StoryTeller Thanks. Ritchie was right. Qualifiers suck. :D – Petr Skocik Jul 08 '18 at 12:44
  • @StoryTeller I take it back. The standard sayeth "Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements." (http://port70.net/~nsz/c/c11/n1570.html#6.2.5p28). The union approach should be OK. – Petr Skocik Jul 08 '18 at 12:47
  • @PSkocik - You haven't proven `const B` and `B` are compatible types. Spoiler: They aren't https://port70.net/~nsz/c/c11/n1570.html#6.7.3p10 – StoryTeller - Unslander Monica Jul 08 '18 at 12:50
  • @StoryTeller They aren't. But `B` and `B` are compatible types by definition and so we a pointer to a qualified version of B and a pointer to an unqualified version of B, and those must be indentically represented as per 6.2.5p28. – Petr Skocik Jul 08 '18 at 12:52
  • `B` is compatible to `B`, not to `const B`. – StoryTeller - Unslander Monica Jul 08 '18 at 12:52
  • *"For two qualified types to be compatible, both shall have the identically qualified version of a compatible type"* - One is a `const B` the other is a `B`, they are not identically qualified – StoryTeller - Unslander Monica Jul 08 '18 at 12:53
  • @StoryTeller You're repeating what I said at the beginning of my last comment. – Petr Skocik Jul 08 '18 at 12:56
  • @PSkocik - No, I'm disputing it. Read carefully the passage I quoted. "**identically qualified**" is key. – StoryTeller - Unslander Monica Jul 08 '18 at 12:56
  • @StoryTeller Once again, `B` & `B` are compatible types `const B` and `B` aren't compatible types but they're a qualified and an unqualified version of compatible types. 6.2.6p28 speaks about "pointers to qualified and unqualified versions of compatible types". `B const*` and `B*` fit that definition. – Petr Skocik Jul 08 '18 at 13:01
  • These two pieces of wording feel self contradictory. The object types aren't compatible, but you may pun them, cause the pointers are compatible. Ugh – StoryTeller - Unslander Monica Jul 08 '18 at 13:32
4

No that is not legal for a simple reason, casts are only allowed for scalar types, C11 6.5.4 "Cast Operators":

Unless the type name specifies a void type, the type name shall specify atomic, qualified, or unqualified scalar type, and the operand shall have scalar type.

Your type is a union type so this is a constraint violation and no C compiler should ever accept this.

If you just want to cast away constness, just do so, that is use (Boo*)constBoo. But note that you do that on your own risk, casting a spell tells the compiler that you pretend that you know what you are doing.

In most of the cases, the behavior of the program is undefined when you use such casted pointers, very bad things can happen.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • 1
    "Your type is a union type so this is a constraint violation" -- Correct. "and no C compiler should ever accept this." -- Wrong. The standard does not mandate an error for a constraint violation, it only requires a diagnostic. A compiler is allowed to issue a warning and accept it as an extension. –  Jul 08 '18 at 13:31
  • Thanks for referencing the standard part that forbids the case. Unfortunately "(Boo*)constBoo" won't work with -Wcast-qual on GCC compilers which is why I came up with this construct in the first place. I prefer to be as explicit about the cast as possible and preferably ensure a type safety similar to const_cast C++ operator. In ideal world I would not need it, but some system APIs have incorrectly defined prototypes that make some form of a cast necessary. – Filip Navara Jul 08 '18 at 13:40