1

Implicit cast from short* to int* prints the warning about incompatible pointer type (and I understand why).

Implicit cast from enum* to int* prints same warnig.

There's a tool snacc that generates the following code:

typedef enum
    {
        CHARGINGCALLING = 0,
        CHARGINGCALLED = 1,
        NONECHARGING = 2
    } ChargedParty; /* ENUMERATED { CHARGINGCALLING (0), CHARGINGCALLED (1), NONECHARGING (2) }  */

typedef struct MSOriginatingSMSinSMS_IWMSC /* SET */
{
    ChargedParty* chargedParty; /* [6] IMPLICIT ChargedParty OPTIONAL */
} MSOriginatingSMSinSMS_IWMSC;

#define BEncChargedPartyContent BEncAsnEnumContent

int BEncMSOriginatingSMSinSMS_IWMSCContent (BUF_TYPE b, MSOriginatingSMSinSMS_IWMSC *v) {
    BEncChargedPartyContent (b, (v->chargedParty));
    ...
}

A header file shipped with this tool:

int BEncAsnIntContent (BUF_TYPE b, int *data);
#define BEncAsnEnumContent BEncAsnIntContent

The call to BEncChargedPartyContent prints the warning.

Can I modify the declaration of BEncAsnEnumContent so it accepts without a warning pointers to any enum, but not void* or short*?

Of course using sed I could replace the macro BEncChargedPartyContent with a static function:

static AsnLen BEncChargedPartyContent (BUF_TYPE b, ChargedParty *data)
{
    return BEncAsnEnumContent(b, (int*)data);
}

But there're too many of them.

basin
  • 3,949
  • 2
  • 27
  • 63
  • [mcve] please. If possible without that ASN #defines and other unnecessary stuff, just stripped down to the minimum. – Werner Henze Dec 15 '15 at 09:02
  • This might be of interest for you: http://stackoverflow.com/questions/1113855/is-the-sizeofenum-sizeofint-always – Werner Henze Dec 15 '15 at 09:03
  • 1
    You could simply disable this warning in the places it occurs, using `#pragma GCC diagnostic ignored`. – John Zwinck Dec 15 '15 at 09:10
  • `int BEncAsnIntContent (BUF_TYPE b, int *data));` has mismatched parentheses – M.M Dec 15 '15 at 09:18
  • @JohnZwinck this code is actually illegal... "solving" it by hiding the warning amounts to relying on undefined behaviour. – M.M Dec 15 '15 at 09:19
  • @basin there's no way to specify "any enum". In general, enums may be different sizes, e.g. the compiler might use 1 byte for an enum with only small enumerators defined, 2 bytes for a medium-size one, and 4 or even 8 bytes for one with large enumerators. This entire design relies on specific non-standard compiler behaviour. – M.M Dec 15 '15 at 09:21
  • 1
    @M.M: All you need to make this code "safe" in practice on GCC is a static assertion that `sizeof(EachEnum) == sizeof(int)`. Of course this may not be technically 100% guaranteed to work on all platforms ever, but in practice it will work fine. – John Zwinck Dec 15 '15 at 11:36
  • The code excerpt is missing the definition of `ChargedParty`. – Armali Oct 26 '17 at 06:20
  • @Armali fixed.. – basin Oct 26 '17 at 08:54

2 Answers2

0

Your own proposal with a static function sounds not so bad.

Can I modify the declaration of BEncAsnEnumContent so it accepts without a warning pointers to any enum, but not void* or short*?

If you want, you can use a static assertion as John Zwinck hinted at.

#define BEncAsnEnumContent(b, d)        ({\
    _Static_assert(sizeof(int) == sizeof *(d), "wrong data size");\
        BEncAsnIntContent(b, (int *)d); })

What you think of in your comment below is a viable alternative with the advantage that it allows enumerations of different sizes; this is how I understand what you mean:

#define BEncAsnEnumContent(b, d)    MyEncAsnEnumContent(b, *(d))
static int MyEncAsnEnumContent(BUF_TYPE b, int val)
{
    return BEncAsnIntContent(b, &val);
}
Armali
  • 18,255
  • 14
  • 57
  • 171
  • A block inside parentheses? Since when is it allowed? – basin Oct 27 '17 at 09:55
  • @basin - Be aware that we are talking about GCC, where this has been possible for, I guess, decades. – Armali Oct 27 '17 at 10:00
  • 1
    I think of a static function `MyEncAsnEnumContent(BUF_TYPE b, int val)` that accepts a value instead of a pointer. Dereference the value in `BEncAsnEnumContent`, call `MyEncAsnEnumContent`, and pass `&val` to `BEncAsnIntContent()` – basin Oct 27 '17 at 10:02
  • @basin - Yes, using that `MyEncAsnEnumContent()` could also be made to work, but what about your requirement that `BEncAsnEnumContent()` shall not accept `short *`? – Armali Oct 27 '17 at 11:00
  • Sorry, I don't quite understand. In your question post you wrote _… modify the declaration of `BEncAsnEnumContent` so it accepts without a warning pointers to any enum, but not `void*` or `short*`_; this sounds to me like you wanted to trap those cases where a pointer to `void` or `short` is erroneously passed to `BEncAsnEnumContent()`. Now you speak of _the size of the enum_; this sounds to me like you don't cater for anything else than `enum`s, but you now take `enum`s of different sizes into account, which can occur only when using special GCC directives. Am I misinterpreting what you say? – Armali Oct 27 '17 at 11:58
  • 1
    No, you understand correctly. Safe enum pointers are more important than not accepting `short*`. And non-numeric pointers like `void*` cannot be dereferenced to `int`, which is good – basin Oct 27 '17 at 12:54
  • Ah, I overlooked that enumerations could have different sizes. I'll add what I understand you think of as an alternative to this answer; I hope you don't mind. – Armali Oct 27 '17 at 13:28
0

The enumeration constants, that is the list of values in your enum declaration, are guaranteed to be of type int. However, this does not apply to the enum variable itself. An enum need not be compatible with int nor with another, different enum type variable. The size can vary from case to case and from compiler to compiler.

This is the root of the problem. If you mix enum and int, or two different enums with each other, all is fine if they have the same size. They are then compatible and you can convert pointers from one of the types to the other without problems.

However, if they are not of the same size, you cannot do this. It would give incompatible types: you would violate the strict aliasing rule and there might also be alignment issues. Plus the obvious: if you try to read a large chunk of data from a location where only a small chunk of data is stored, there's no telling what you will end up with.

The reliable solution is to change your function to simply use an integer type instead of a pointer:

int BEncAsnIntContent (BUF_TYPE b, int data);

There doesn't seem to be a reason why they pass the parameter through a pointer in the first place. Keep it simple.

Lundin
  • 195,001
  • 40
  • 254
  • 396