【问题标题】:Typecast all arguments in a variadic macro对可变参数宏中的所有参数进行类型转换
【发布时间】:2014-11-20 13:24:15
【问题描述】:

我想用sscanf来解析一个长字符串..
要解析的数据将存储在一个结构中,其成员的类型都是time_t
不幸的是,没有任何格式字符串来标记time_t,所以我只想将所有time_t * 参数类型转换为unsigned long long *,因为它是一个非常长的字符串,有很多参数,每个参数一个一个只会弄乱我的编辑器屏幕..

所以,我创建了一个宏来简化这个:

#define typecast(type, ...) (type) __VA_ARGS__

我这样调用它:

sscanf(string, PARSE_STRING,
    typecast(unsigned long long *, /* my arguments */));

虽然这会扩展为:

sscanf(string, PARSE_STRING,
    (unsigned long long *) /* arg1 */, (unsigned long long *) /* arg2 */, /* and so on */);

但是在用gcc -E检查之后,我发现它扩展到了这个:

sscanf(string, PARSE_STRING,
    (unsigned long long *) /* arg1 */, /* arg2 */, /* and so on */));

如何使用可变参数函数实现我想要的扩展功能?

【问题讨论】:

  • 你可能会得到一些灵感here
  • This question也有一些相关的答案。
  • 请注意,time_t 通常是有符号整数类型。如果您正在扫描,则必须确保该类型与 time_t 兼容——它在某种程度上是特定于平台的。
  • @JonathanLeffler:我不知道time_t 已签名,因为时间要么为零,要么为正,但无论如何,我确信我正在读取的数据永远不会有负值。
  • 传统上,时间 0 是 1970-01-01 00:00:00 Z(或 +00:00)。 mktime() 函数在出错时返回 (time_t)-1。对于 32 位有符号类型(旧 32 位系统上的long),可表示时间的范围大致是从 1901-12-13 (0x80000000) 到 2038-01-18 (0x7FFFFFFF)。这就是为什么您有时会看到有关 Y2038 危机的 cmets; 32 位签名的 time_t 将翻转。大多数系统现在使用 64 位 time_t 来避免该问题(以及 Y10K 问题等)。

标签: c arguments c-preprocessor variadic-macros


【解决方案1】:

使用提升

#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>

#define CAMMA(r, data, i, elem) BOOST_PP_COMMA_IF(i)elem
#define CAST(r, data, elem) (data)elem
#define typecast(type, ...) BOOST_PP_SEQ_FOR_EACH_I(CAMMA, , BOOST_PP_SEQ_TRANSFORM(CAST, type, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)))

【讨论】:

  • @AmrAyman boost/preprocessor 也可以在 C 中使用。(因为只使用预处理器)
【解决方案2】:

我看到 C 预处理器代码在可变参数宏的参数上循环,我不希望它出现在我的代码中。但是,在您的情况下,您可以采取以下解决方案。我正在展示最多可用于 4 个参数的宏,您肯定可以将其扩展到任何常量。

#define typecast4(type, a1, a2, a3, a4, ...) ((type)a1), ((type)a2), ((type)a3), ((type)a4)
#define typecast(type, ...) typecast4(type, __VA_ARGS__, NULL, NULL, NULL, NULL)

这个特殊的解决方案总是生成 4 个参数。请注意,在您的情况下,这无关紧要,因为 scanf 接受并安全地忽略超出输入格式的任何参数。所以,下面的代码编译:

int main() {
  time_t x,y;
  scanf("%lld, %lld", typecast(long long *, &x, &y));
  ...
}

但是,我担心从time_t *long long * 的演员阵容。没有地方写到time_t 必须由long long 表示。如果它在某些平台上由int 表示,则之前的代码将是错误的。扫描您的值的正确方法是:

int main() {
  time_t x,y;
  long long lx, ly;
  scanf("%lld, %lld", &lx, &ly);
  x = lx;
  y = ly;
  ...
}

更正确,不需要任何可变参数宏。

【讨论】:

    猜你喜欢
    • 2014-12-15
    • 1970-01-01
    • 2013-05-21
    • 2015-05-19
    • 1970-01-01
    • 1970-01-01
    • 2012-12-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多