[C] Linux 中 htonl(常量) 不能在编译期计算出来吗?

2023-02-08 16:31:12 +08:00
 iqoo

Mac 中编译通过的代码在 Linux 中编译不通,问题在于 enum 中使用了 htonl

enum A {
  X = htonl(1),
};

报错:

./****: error: expression is not an integral constant expression
  X = htonl(1),
      ^~~~~~~~
/usr/include/netinet/in.h:404:21: note: expanded from macro 'htonl'
#   define htonl(x)     __bswap_32 (x)
                        ^~~~~~~~~~~~~~
./****: note: non-constexpr function '__bswap_32' cannot be used in a constant expression

而 Mac 中 htonl 是这样的:

#define htonl(x)        __DARWIN_OSSwapInt32(x)

#define __DARWIN_OSSwapInt32(x) \
    (__builtin_constant_p(x) ? __DARWIN_OSSwapConstInt32(x) : _OSSwapInt32(x))

如果是常量,直接用 __DARWIN_OSSwapConstInt32 宏算出结果,不是常量则在运行时 bswap 计算。所以 htonl(常量) 能在 enum 中使用。

为什么 Linux 没有实现这个特性?

Linux: 5.4.0-126-generic   (clang version 10.0.0-4ubuntu1)
MacOS: 22.1.0 Darwin       (Apple clang version 14.0.0)
723 次点击
所在节点    程序员
5 条回复
tool2d
2023-02-08 16:41:11 +08:00
我试了一下,这样写可以:

enum A {
X = __builtin_bswap32(1),
};

估计还是为了什么兼容性吧。
minami
2023-02-08 16:53:00 +08:00
那你用__constant_htonl 呗
ysc3839
2023-02-08 21:59:38 +08:00
C++的话自己实现个 constexpr 的即可,C 不行。
ysc3839
2023-02-08 22:08:31 +08:00
__DARWIN_OSSwapConstInt32 这个应该是编译器的 intrinsic 。按照 BSD socket 的标准,htonl 应该是个函数来的,Linux 实现成了一个函数,所以不支持。
你要编译期且跨平台的话,用 C++的 std::endian 加上 if constexpr 是最简单的。
C 的话可以用编译器提供的字节序宏以及翻转字节序的 intrinsic 。
iqoo
2023-02-09 11:55:16 +08:00
@ysc3839 我用 gcc/clang 的 __builtin_constant_p 实现了,可以在编译期判断是不是常量。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/914319

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX