【问题标题】:Implementing min() and max() in Clang without double evaluation在没有双重评估的情况下在 Clang 中实现 min() 和 max()
【发布时间】:2013-12-18 15:17:31
【问题描述】:

min 函数的经典预处理器版本看起来像

#define min(a, b) ((a) < (b) ? (a) : (b))

这会让你面临双重评估——在这种情况下你执行min(f(), g()),忘记了fg 有副作用,并且你必须花费数小时试图弄清楚为什么你的函数运行两次.为了防止这种情况,你可以这样做

#define min(a, b) ({__typeof__(a) _a = (a); \
    __typeof__(b) _b = (b); \
    _a < _b ? _a : _b;})

这在 GCC 下效果很好,但是如果你通过 Clang 运行它并设置 -Wgnu ——这组警告属于 -pedantic 的保护伞——你会得到类似的错误

test.c:5:10: error: use of GNU statement expression extension [-Werror,-Wgnu]
        int a = min(3, 7);
                ^
test.c:1:22: note: expanded from macro 'min'
#  define min(a, b) ({__typeof__(a) _a = (a); __typeof__(b) _b = (b); _a < _b ? _a : _b;})

是否可以定义这些宏以防止双重评估,这在-pedantic 下是 Clang 可以接受的? (是的,您可以使用-Wno-gnu 禁用警告,在这种情况下,Clang 可以毫无问题地处理语句表达式。我之所以这么问是因为我和clang -pedantic 一样,对我自己太挑剔了。)

编辑:我正在使用 C。我也标记了这个 C++,因为我认为一个解决方案可能适用于 C++ 以及 C。糟糕——忘记了模板!很抱歉有歧义。

【问题讨论】:

  • 我很好奇为什么 CC++ 标签都是你的目标语言?答案因语言而异。
  • 不要使用预处理器,期间。内联函数以更明显的方式更好地完成工作。而且我认为在 C++ 中你甚至可以使用模板来避免为不同类型创建多个函数。
  • 为什么这个问题可以接受 C++ 答案,并且明确声明需要 C 并且存在 C 版本?
  • @bdesham,我不明白,该链接并没有增加您在问题中已有的内容。请您详细说明,为什么 C 的答案不太令人满意?现在看来,这是对 C 问题的 C++ 答案,因此它不会为许多其他问题提供帮助。
  • @bdesham 这是样板文件,您只需编写一次。

标签: c clang


【解决方案1】:

如果你真的是在用 C++ 编写代码,那么不要使用预处理器

template <typename T>
const T min(const T& a, const T& b)
{
   return (b < a) ? b : a;
}

(请注意,我交换了ba,因此如果两者相等,您将获得左侧操作数。)

否则no, not really

【讨论】:

  • @Lundin:因为&lt; 运算符不会选择两个操作数之一。它给你一个布尔值。向我们展示您的天才 C 解决方案。
  • 这是一篇文章,其中 Andrei Alexandrescu 对编写 min/max 函数模板的正确方法非常感兴趣:drdobbs.com/generic-min-and-max-redivivus/184403774
  • 即使作为一个 C++ 答案,这也不是一个好的答案,因为这并没有捕捉到 ab 可能有不同类型的情况。
  • @bdesham 如果您使用的是 C,那么 1)为什么将其标记为 C++ 以及 2)为什么接受 C++ 的答案。你没有任何意义。
  • 它是用C++编写的,不会用C编译。
【解决方案2】:

我认为这可以作为 C11 解决方案。

inline
int min(int const x, int const y)
{
    return y < x ? y : x;
}

inline
unsigned minu(unsigned const x, unsigned const y)
{
    return y < x ? y : x;
}

inline
long minl(long const x, long const y)
{
    return y < x ? y : x;
}

inline
unsigned long minul(unsigned long const x, unsigned long const y)
{
    return y < x ? y : x;
}

inline
long long minll(long long const x, long long const y)
{
    return y < x ? y : x;
}

inline
unsigned long long minull(unsigned long long const x, unsigned long long const y)
{
    return y < x ? y : x;
}

inline
float minf(float const x, float const y)
{
    return y < x ? y : x;
}

inline
double mind(double const x, double const y)
{
    return y < x ? y : x;
}

inline
long double minld(long double const x, long double const y)
{
    return y < x ? y : x;
}

#define MIN(X, Y) (_Generic((X) + (Y),   \
    int:                min,             \
    unsigned:           minu,            \
    long:               minl,            \
    unsigned long:      minul,           \
    long long:          minll,           \
    unsigned long long: minull,          \
    float:              minf,            \
    double:             mind,            \
    long double:        minld)((X), (Y)))

【讨论】:

  • +1 但您的 _Generic 表达式太复杂了。 _Generic((X)+(Y), int : min, unsigned : minu, ...)((X), (Y)) 就足够了。你也忘记了浮点类型。
  • @JensGustedt 谢谢。我不认为有办法让它为指针工作,虽然不需要void* 演员。
  • 指针可以通过default 案例完成,只是现在的选择表达式不适用于指针:(
  • @JensGustedt 我认为如果您尝试传递 struct 或其他内容,您会丢失更好的错误消息。
【解决方案3】:

Simple 发布的 C11 解决方案看起来很理想,但如果您没有 C11 编译器,您仍然可以定义一个具有内在类型安全性的宏(无论如何 C 都是类型安全的):

#define MIN(type, X, Y) min_ ## type(X, Y)

这个宏只允许传递实现的类型,否则你会得到一个编译器错误。

例子:

#define MIN(type, X, Y) min_ ## type(X, Y)

long  min_long  (long x, long y);
char  min_char  (char x, char y);
float min_float (float x, float y);

int main()
{
  long  min_l = MIN (long, 5L, 10L);
  char  min_c = MIN (char, 'A', 'B');
  float min_f = MIN (float, 666.66f, 3.14f);

  printf("%ld\n", min_l);
  printf("%c\n", min_c);
  printf("%f\n", min_f);
}

char  min_char  (char x, char y)    { return x < y ? x : y; }
long  min_long  (long x, long y)    { return x < y ? x : y; }
float min_float (float x, float y)  { return x < y ? x : y; }

现在如果你用MIN(int, 1, 2) 执行上面的宏,你会得到一个编译器错误:“min_int, no such function exists”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-22
    • 1970-01-01
    • 2022-06-20
    • 2020-06-03
    • 1970-01-01
    • 2023-03-19
    • 2019-04-15
    • 2017-02-26
    相关资源
    最近更新 更多