我试图用C(唉,而不是C ++)编写宏来捕获某些错误,特别是如果我传递了错误类型的名称。
例如,使用
typedef int APLNELM;
typedef int APLRANK;
#define IsScalar(a) ((a) == 0)
APLNELM AplNelm = 0;
APLRANK AplRank = 0;
调用IsScalar (AplRank)
是正确的,因为Scalar是一个Rank概念,但是IsScalar (AplNelm)
是错误的,因为Scalar不是#elements概念。
有些聪明的人可以找到一种方法来编写IsScalar
宏,以便检查传递给它的名称的类型,以确保它的类型为APLRANK
吗?如果能提供解决方案,请随意以任何等效的方式重写原始示例。
答案 0 :(得分:0)
如果要定义两个不同的整数类型,则直接typedef
方法会失败,因为typedef
会为同一类型创建同义词,而不会创建新类型。
有一种方法可以创建不同的整数类型,但即使在这种情况下,也无法检测"他们通过他们的价值观
例如,请遵守以下代码:
enum myint1_e {min1 = -32767, max1 = 32767};
enum myint2_e {min2 = -32767, max2 = 32767};
typedef enum myint1_e integer1_t;
typedef enum myint2_e integer2_t;
integer1_t x1 = 0;
integer2_t x2 = 0;
现在,enum myint1_t
和enum myint2_t
这两种类型是不同的整数类型
见C11:6.7.2.3。(par.5):
枚举类型的两个声明位于不同的范围或使用不同的标记声明了不同的类型。
所以,他们的typedef
版本也是不同的
因此,变量x1
和x2
具有不同的类型
整数值0可以分配给两个变量。
现在,如果你想检查一个变量的类型是你想要的那个,你可以尝试这样做:
#define VERIFY_INT1TYPE(a) ((integer1_t*)(0) == (&a))
但是这种方法只提供一条警告信息,而不是"比较值为false"你期望的。
说明:虽然整数类型在某种程度上可以在赋值操作中互换,但另一方面它们的"指向"版本总是不同的类型。因此,像x1 == x2
这样的句子根本没有任何问题,但是两个不同指针类型的值的比较会引发警告信息。
备注:表达式(integer1_t*)(0)
是强制转换为integer1_t*
类型的NULL指针。
示例:
VERIFY_INT1TYPE(x2);
此示例在使用GCC编译时发出警告消息。
答案 1 :(得分:0)
如果这两种类型只能传递到isScalar
宏,那么你可以这样做:
#include <stdio.h>
struct APLNELM {
int nelm;
char a[1];
};
struct APLRANK {
int rank;
char a[2];
};
#define isScalar(b) (sizeof b.a == 2)
int main(void) {
// your code goes here
struct APLNELM temp1;
struct APLRANK temp2;
printf("%d\n", isScalar(temp1));
printf("%d\n", isScalar(temp2));
return 0;
}
此代码的输出是
0
1
答案 2 :(得分:0)
这样可行,但我强烈建议您不要使用它,因为它不可维护:
typedef int APLNELM;
typedef int APLRANK;
typedef unsigned int TYPETRAITS;
#define TRAIT_SCALAR 0x1
#define TYPETRAITS_APLNELM TRAIT_SCALAR /*whatever else you want, up to 32 traits*/
#define TYPETRAITS_APLRANK 0/*whatever else you want, up to 32 traits*/
#define GET_TYPE_TRAITS(X) TYPETRAITS_##X
#define IS_SCALAR(X) (X & TRAIT_SCALAR)
#define IS_TYPE_SCALAR(X) IS_SCALAR(GET_TYPE_TRAITS(X))
int main()
{
const int aplnelm_traints = GET_TYPE_TRAITS(APLNELM);
const int aplrang_traints = GET_TYPE_TRAITS(APLRANK);
const bool is_aplnelm_scalar = IS_TYPE_SCALAR(APLNELM);
const bool is_aplrang_scalar = IS_TYPE_SCALAR(APLNELM);
}
答案 3 :(得分:0)
我放弃了以下代码(需要GNU扩展程序:typeof
和Statement Exprs):
#include <stdio.h>
typedef int APLNELM;
typedef int APLRANK;
#define IsScalar(a) \
({ \
/* Override typedefs in block scope */ \
typedef char APLNELM; \
typedef int APLRANK; \
/* Create variable with typeof(a) type; \
* then compare it by sizeof with APLNELM */ \
typeof(a) b; sizeof b == sizeof(APLNELM); \
})
int main(void)
{
APLNELM a = 5;
APLRANK b = 5;
printf("IsScalar: %d\n", IsScalar(a) ? 1 : 0);
printf("IsScalar: %d\n", IsScalar(b) ? 1 : 0);
return 0;
}
事实是,typeof(a)
实际上并未被APLNELM
或APLRANK
取代。 C不是动态语言,我同意struct
概念更适合这种区分。
答案 4 :(得分:0)
一种可能性是将整数包装在单字段结构中,以强制执行强类型。为了避免最终的生产代码不是最理想的,使用不同的宏定义进行两次编译;一次使用结构来检测错误,一次没有结构以获得最佳代码。
#ifdef STRONG_TYPING
#define TYPE(basetype, field) struct { basetype field; }
#define INITIALIZER(value) {(value)}
#define AS_BASETYPE(field, value) ((value).field)
#else
#define TYPE(basetype, field) basetype
#define INITIALIZER(value) (value)
#define AS_BASETYPE(field, value) (value)
#endif
typedef TYPE(int, alpnelm) APLNELM;
typedef TYPE(int, alprank) APLRANK;
#define IsScalar(a) (AS_BASETYPE(aplrank, a) == 0)
定义STRONG_TYPING
后,IsScalar(SomeAplNelm)
会产生编译错误。没有STRONG_TYPING
,结构的开销将完全消失。当然,在链接之前必须使用相同的定义编译所有模块,否则您的可执行文件可能会崩溃。
在程序代码中,您必须在使用宏时应用一些规则。声明示例:
APLNELM MyAplNelm1;
APLNELM MyAplNelm2 = INITIALIZER(0);
分配:
AS_BASETYPE(aplnelm, MyAplNelm1) = 0;
AS_BASETYPE(aplnelm, MyAplNelm2) = AS_BASETYPE(aplnelm, MyAplNelm1);
仍允许在不同的“强”类型之间交换价值;只要为每个单独的值指定正确的类型(结构中字段的名称)。
AS_BASETYPE(aplnelm, MyAplNelm2) = AS_BASETYPE(aplrank, MyAplRank);
请注意,您始终需要AS_BASETYPE
才能访问其中一种“强”类型的变量。这将使代码更加冗长(请随意为宏选择较短的名称),但这没有任何问题。这只是你要添加的元数据的概念;它实际上应该提高可维护性。