Использование предварительной обработки C для получения целочисленного значения строки - PullRequest
10 голосов
/ 02 марта 2012

Как мне создать макрос C , чтобы получить целочисленное значение строки? Конкретный вариант использования вытекает из вопроса здесь . Я хочу изменить код следующим образом:

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 ]

5 голосов
/ 02 марта 2012

Вот решение на чистом 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;
}
2 голосов
/ 02 марта 2012

РЕДАКТИРОВАТЬ: Этот ответ может быть полезным, поэтому я не удаляю его, но конкретно не отвечаю на вопрос.Он конвертирует строки в числа, но не может быть помещен в перечисление, потому что не вычисляет число во время компиляции.

Что ж, поскольку ваши целые числа 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)

Что в основном делает для проверки на каждом байте, находится ли он в пределеstring и, если это так, то выдает соответствующий байт.

Обратите внимание: , что это работает только для литеральных строк.

Если вы хотите иметь возможность преобразовывать любыеstring, вы можете указать длину строки:

#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))
1 голос
/ 02 марта 2012

Если вы можете использовать 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") };

будет работать и вычислять константу во время компиляции.

0 голосов
/ 02 марта 2012

Некоторая рекурсивная магия шаблонов может помочь. Не создает код, если константы известны во время компиляции.

Возможно, вы захотите следить за временем сборки, если вы используете его в гневе.

// 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;
}
...