是否有GCC警告可以指示任何类型更改?

j91ykkif  于 2022-11-13  发布在  其他
关注(0)|答案(3)|浏览(142)

我的代码中有以下类型定义:

#define FOO_OFF 0
#define FOO_ON 1
typedef uint8_t foo;

#define BAR_NO  0
#define BAR_YES 1
#define BAR_UNKNOWN 255
typedef uint8_t bar;

这两种类型虽然具有相同的基础类型,但它们携带的信息不同。
实际上,如果代码中的任何人执行以下操作,我希望得到警告:

foo foovar = FOO_OFF;

void get_bar(bar *outvar)
{
    // assigning the bar variable a foo variable content
    *outvar = foovar;
}

我在gcc中找不到任何这样的警告选项,我遇到了accross -Wconversion,但只有在有可能丢失信息的情况下才会发出警告,而在我的示例中并非如此。
有人知道我能做些什么吗?显然,当真正需要类型更改时,应该可以进行强制转换。

sxissh06

sxissh061#

简短的回答是否定的,您不能这样做。typedef声明了一个别名,而不是一个新类型,因此任何遵守该标准的编译器都不能拥有您现在想要的特性。
不过,您可以借由引入新型别、使用枚举或结构来达成。
如果您使用C语言,则可以轻松地从一个枚举转换到另一个枚举。
因为struct的第一个元素的地址也是struct的地址,所以你可以通过转换struct地址,然后用它的新类型取消引用指针,从而在int8或另一个struct之间转换它。(*((dest_type *)&value)

vptzau2j

vptzau2j2#

我非常喜欢问题Strongly typed using and typedef的作者所在团队提出的解决方案,因为它非常简单:

#define STRONG_TYPEDEF(T, D)                                 \
    class D : public T {                                     \
    public:                                                  \
        template<class... A> explicit D(A... a) : T(a...) {} \
        const T& raw() const { return *this; }               \
    }

不幸的是,它不能处理原始类型,但它与BOOST_STRONG_TYPEDEF类似,可以。
下面是一个小例子和两者的退化“比较”:

#include <iostream>

#define USE_BOOST true

#if USE_BOOST

#include <boost/serialization/strong_typedef.hpp>
BOOST_STRONG_TYPEDEF(std::string, foo);
BOOST_STRONG_TYPEDEF(std::string, bar);

BOOST_STRONG_TYPEDEF(int, myInt); // Boost allows primitives

#else

#define STRONG_TYPEDEF(T, D)                                 \
    class D : public T {                                     \
    public:                                                  \
        template<class... A> explicit D(A... a) : T(a...) {} \
        const T& raw() const { return *this; }               \
    }

STRONG_TYPEDEF(std::string, foo);
STRONG_TYPEDEF(std::string, bar);

// STRONG_TYPEDEF(int, myInt); // this one just classes

#endif

int main()
{
    std::string a = "abc";
    foo b{"abc"};
    bar c = static_cast<bar>(a);
    std::string d = c; // we can assign back to base type

#if USE_BOOST // Boost
    myInt x{4};  // only allows this type of initialization
    switch (x) { // allows primitives, so `switch` works
        case 1:
            std::cout << 1 << std::endl;
            break;
        case 4:
            std::cout << 4 << std::endl;
            break;
    }
#else // Boost don't allow the following:
    if (b == c) {                    // comparing
        std::cout << c << std::endl; // printing
    }
#endif

    /* But we can't
       foo e = a; // assign base type to new type
       foo f = c; // assign one type to another
    */

    return 0;
}
mbjcgjjk

mbjcgjjk3#

如果大小不一定是一个字节,我们可以(ab)使用指针:

typedef struct foo *rolloverdetection;
typedef struct bar *rolloverdetected;

#define ROLLOVERDETECTION_OFF    ((rolloverdetection) 0)
#define ROLLOVERDETECTION_ON     ((rolloverdetection) 1)

#define ROLLOVERDETECTED_NO      ((rolloverdetected) 0)
#define ROLLOVERDETECTED_YES     ((rolloverdetected) 1)
#define ROLLOVERDETECTED_UNKNOWN ((rolloverdetected) 2)

预处理器符号不再是常量表达式,我们不能把它们用作开关标签之类的东西。
一个很好的解决方案是使用C类型安全的enums。这是用“干净的C”编写代码的优点之一:一种非正式的名称,指在一种语言方言中工作,它编译成某种版本的C,以及具有相同行为的某种版本的C
简单地说:

typedef enum {
  ROLLOVERDETECTION_OFF,
  ROLLOVERDETECTION_ON
} rolloverdetection;

typedef enum {
  ROLLOVERDETECTED_NO,
  ROLLOVERDETECTED_YES,
  ROLLOVERDETECTED_UNKNOWN = 255
} rolloverdetected;

在C中,您仍然可以将ROLLOVERDETECTED_YES赋给rolloverdetection类型的变量,但在C中就不行了。
如果将代码编译为C
,则可以使用C编译器检查这些冲突,即使代码的发布版本不使用C
如果以8位存储值很重要,我记得GCC支持枚举类型的位域作为扩展(ISO C中没有):

struct whatever {
  rolloverdetected roll_detect : 8;
};

C++枚举不是完全类型安全的;从enum成员到整数类型的隐式转换是可能的:

int roll = ROLLOVERDETECTION_ON;

而不是在相反方向上。
顺便说一句,如果你用C和C++编译,其他的技术也会出现,比如能够使用更细微的类型转换:

#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
#else
#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
#define convert(TYPE, EXPR) ((TYPE) (EXPR))
#define coerce(TYPE, EXPR) ((TYPE) (EXPR))
#endif

所以现在,举例来说,我们可以

strip_qual(char *, str)

在C语言中,这只是一个不安全的(char *) str类型转换。在C中,上述宏会产生const_cast<char *>(str)。因此,如果str一开始是const char *,但后来有人将其更改为const wchar_t *,C编译器将诊断上述类型转换。然而,我们的项目并不 * 需要 * C编译器来构建。
与此同时,如果您使用GCC,其C
前端具有-Wold-style-cast,它将查找代码中使用(type) value转换标记法的所有位置。

相关问题