如何创建 C 宏来获取字符串的整数值?具体用例来自问题here。我想改变这样的代码:
enum insn {
sysenter = (uint64_t)'r' << 56 | (uint64_t)'e' << 48 |
(uint64_t)'t' << 40 | (uint64_t)'n' << 32 |
(uint64_t)'e' << 24 | (uint64_t)'s' << 16 |
(uint64_t)'y' << 8 | (uint64_t)'s',
mov = (uint64_t)'v' << 16 | (uint64_t)'o' << 8 |
(uint64_t)'m'
};
对此:
enum insn {
sysenter = INSN_TO_ENUM("sysenter"),
mov = INSN_TO_ENUM("mov")
};
INSN_TO_ENUM
扩展为相同代码的位置。性能会相同,但可读性会大大提高。
我怀疑在这种形式下它可能是不可能的,因为C预处理器无法进行字符串处理,所以这也是一个不可取但可接受的解决方案(变量参数宏):
enum insn {
sysenter = INSN_TO_ENUM('s','y','s','e','n','t','e','r'),
mov = INSN_TO_ENUM('m','o','v')
};
答案 0 :(得分:4)
这是一个编译时纯C解决方案,您表示可以接受。您可能需要延长它以获得更长的助记符。我将继续考虑所需的一个(即INSN_TO_ENUM("sysenter")
)。有趣的问题:)
#include <stdio.h>
#define head(h, t...) h
#define tail(h, t...) t
#define A(n, c...) (((long long) (head(c))) << (n)) | B(n + 8, tail(c))
#define B(n, c...) (((long long) (head(c))) << (n)) | C(n + 8, tail(c))
#define C(n, c...) (((long long) (head(c))) << (n)) | D(n + 8, tail(c))
#define D(n, c...) (((long long) (head(c))) << (n)) | E(n + 8, tail(c))
#define E(n, c...) (((long long) (head(c))) << (n)) | F(n + 8, tail(c))
#define F(n, c...) (((long long) (head(c))) << (n)) | G(n + 8, tail(c))
#define G(n, c...) (((long long) (head(c))) << (n)) | H(n + 8, tail(c))
#define H(n, c...) (((long long) (head(c))) << (n)) /* extend here */
#define INSN_TO_ENUM(c...) A(0, c, 0, 0, 0, 0, 0, 0, 0)
enum insn {
sysenter = INSN_TO_ENUM('s','y','s','e','n','t','e','r'),
mov = INSN_TO_ENUM('m','o','v')
};
int main()
{
printf("sysenter = %llx\nmov = %x\n", sysenter, mov);
return 0;
}
答案 1 :(得分:2)
编辑:这个答案可能会有所帮助,所以我不会删除它,但没有具体回答这个问题。它将字符串转换为数字,但不能放在枚举中,因为它不会在编译时计算数字。
好吧,因为你的整数是64位,你只需要担心任何字符串的前8个字符。因此,你可以写东西8次,确保你没有超出字符串界限:
#define GET_NTH_BYTE(x, n) (sizeof(x) <= n?0:((uint64_t)x[n] << (n*8)))
#define INSN_TO_ENUM(x) GET_NTH_BYTE(x, 0)\
|GET_NTH_BYTE(x, 1)\
|GET_NTH_BYTE(x, 2)\
|GET_NTH_BYTE(x, 3)\
|GET_NTH_BYTE(x, 4)\
|GET_NTH_BYTE(x, 5)\
|GET_NTH_BYTE(x, 6)\
|GET_NTH_BYTE(x, 7)
它的作用基本上是检查每个字节是否在字符串的限制内,如果是,则给出相应的字节。
注意:,这仅适用于文字字符串。
如果您希望能够转换任何字符串,可以用它给出字符串的长度:
#define GET_NTH_BYTE(x, n, l) (l < n?0:((uint64_t)x[n] << (n*8)))
#define INSN_TO_ENUM(x, l) GET_NTH_BYTE(x, 0, l)\
|GET_NTH_BYTE(x, 1, l)\
|GET_NTH_BYTE(x, 2, l)\
|GET_NTH_BYTE(x, 3, l)\
|GET_NTH_BYTE(x, 4, l)\
|GET_NTH_BYTE(x, 5, l)\
|GET_NTH_BYTE(x, 6, l)\
|GET_NTH_BYTE(x, 7, l)
例如:
int length = strlen(your_string);
int num = INSN_TO_ENUM(your_string, length);
最后,有一种方法可以避免给出长度,但它需要编译器实际上从左到右计算INSN_TO_ENUM
的短语。 我不确定这是否是标准的:
static int _nul_seen;
#define GET_NTH_BYTE(x, n) ((_nul_seen || x[n] == '\0')?(_nul_seen=1)&0:((uint64_t)x[n] << (n*8)))
#define INSN_TO_ENUM(x) (_nul_seen=0)|
(GET_NTH_BYTE(x, 0)\
|GET_NTH_BYTE(x, 1)\
|GET_NTH_BYTE(x, 2)\
|GET_NTH_BYTE(x, 3)\
|GET_NTH_BYTE(x, 4)\
|GET_NTH_BYTE(x, 5)\
|GET_NTH_BYTE(x, 6)\
|GET_NTH_BYTE(x, 7))
答案 2 :(得分:1)
如果可以在最近的编译器上使用C ++ 11
constexpr uint64_t insn_to_enum(const char* x) {
return *x ? *x + (insn_to_enum(x+1) << 8) : 0;
}
enum insn { sysenter = insn_to_enum("sysenter") };
将在编译期间工作并计算常量。
答案 3 :(得分:0)
一些递归模板魔法可以解决问题。如果在编译时已知常量,则不创建代码。
如果您在愤怒中使用它,可能需要留意您的构建时间。
// the main recusrsive template magic.
template <int N>
struct CharSHift
{
static __int64 charShift(char* string )
{
return string[N-1] | (CharSHift<N-1>::charShift(string)<<8);
}
};
// need to provide a specialisation for 0 as this is where we need the recursion to stop
template <>
struct CharSHift<0>
{
static __int64 charShift(char* string )
{
return 0;
}
};
// Template stuff is all a bit hairy too look at. So attempt to improve that with some macro wrapping !
#define CT_IFROMS(_string_) CharSHift<sizeof _string_ -1 >::charShift(_string_)
int _tmain(int argc, _TCHAR* argv[])
{
__int64 hash0 = CT_IFROMS("abcdefgh");
printf("%08llX \n",hash0);
return 0;
}