使用C预处理获取字符串的整数值

时间:2012-03-02 15:00:58

标签: c macros c-preprocessor

如何创建 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')
};

4 个答案:

答案 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;
}