Хеширование строки во время компиляции (препроцессора) - PullRequest
15 голосов
/ 13 мая 2010

Есть ли способ создать хеш строки во время компиляции, используя препроцессор C / C ++ (или даже шаблон-метапрограммирование)?

например. UNIQUE_SALT("HelloWord", 3DES);

Идея состоит в том, что HelloWorld не будет присутствовать в скомпилированном бинарном файле, это просто хеш.

Редактировать: многие из этих объявлений распространяются на большую кодовую базу.

Ответы [ 8 ]

12 голосов
/ 13 мая 2010

С C ++ 0x это возможно, как описано в ответах в # 1 и # 2 .

В C ++ 03 не было обработки строки времени компиляции. С препроцессором вы не можете разделить строку на токены, с шаблонами вы не можете получить доступ к отдельным символам. Однако обсуждался спекулятивный подход с использованием C ++ 0x .

Что вы могли бы сделать для C ++ 03, так это передать строку символьно (возможно, используя многосимвольные литералы):

foo = hash<3DES, str<'a','b','c'> >::result;
// or:
foo = hash<3DES, str<'abc','def'> >::result;

... или просто сделайте это как шаг перед сборкой.

12 голосов
/ 13 мая 2010

Почему бы не сделать генерацию хеша частью вашего процесса сборки? Вы можете написать простой пакетный файл для генерации хеша (при условии, что у вас есть программа, которая делает такую ​​вещь - если нет, напишите ее) и заставить ее вывести директиву препроцессора, например:

#define MY_HASH 123456789 

в файл .h, который затем включается в ваше приложение.

8 голосов
/ 03 сентября 2014

Хотя это неправильный ответ на вопрос, посмотрите в этой записи блога пример хэш-функции для строк длиной до 256 символов, реализованных исключительно как макрос C:

http://lolengine.net/blog/2011/12/20/cpp-constant-string-hash

Вот актуальный код из блога:

#include <string.h>
#include <stdint.h>
#include <stdio.h>

#define H1(s,i,x)   (x*65599u+(uint8_t)s[(i)<strlen(s)?strlen(s)-1-(i):strlen(s)])
#define H4(s,i,x)   H1(s,i,H1(s,i+1,H1(s,i+2,H1(s,i+3,x))))
#define H16(s,i,x)  H4(s,i,H4(s,i+4,H4(s,i+8,H4(s,i+12,x))))
#define H64(s,i,x)  H16(s,i,H16(s,i+16,H16(s,i+32,H16(s,i+48,x))))
#define H256(s,i,x) H64(s,i,H64(s,i+64,H64(s,i+128,H64(s,i+192,x))))

#define HASH(s)    ((uint32_t)(H256(s,0,0)^(H256(s,0,0)>>16)))

Если вы заранее знаете, что будете использовать его только для статических строк, вы можете заменить strlen () на sizeof ().

6 голосов
/ 13 мая 2010

Это можно сделать с помощью Boost.MPL, но это может быть не тот тип хэша, который вы используете.

http://arcticinteractive.com/2009/04/18/compile-time-string-hashing-boost-mpl/

3 голосов
/ 07 апреля 2011

Я наткнулся на решение, используя хороший старый стандарт C ++ (я не уверен, какую версию он рассматривал, но давайте просто скажем, что это решение работает в Visual Studio). Вот ссылка: ссылка .

Кроме того, вот краткая версия функции JSHash, использующей вышеупомянутую технику. Показанный здесь поддерживает до 4 символов, хотя вы можете добавить столько, сколько хотите.

template<const char A = 0, const char B = 0, const char C = 0, const char D = 0>
struct cHash
{
    template<const char C, size_t hash = 1315423911>
    struct HashCalc
    {
        enum { value = (C == 0) ? hash : hash ^ ((hash << 5) + C + (hash >> 2)) };
    };

    enum { value = HashCalc<D,HashCalc<C,HashCalc<B,HashCalc<A>::value>::value>::value>::value };
};

Как уже отмечалось, поскольку это хэш времени компиляции, вы можете сделать что-то вроде этого:

namespace Section
{
    enum Enum
    {
        Player = cHash<'p','l','a','y'>::value
    };
}

Это не самое элегантное решение, поэтому я планирую провести дополнительные исследования в этой области, однако, поскольку это единственное, что я получил, чтобы работать в VisualStudio2010, я немного ограничен в том, что касается моего текущего проекта ,

3 голосов
/ 13 мая 2010

Даже если этого нельзя (разумно) сделать с препроцессором, если вы использовали строковый литерал или объявили его как static const и не создали постоянных ссылок на него, компилятор, скорее всего, продолжит работу и выполнит все математика для генерации результата и пропуска строки в объектном файле, если вы компилируете с оптимизацией. Самое сложное в том, что вы не можете сделать код для инициализации глобальной или статической переменной слишком сложным, или компилятор скажет: «Эй, вы! Разве вы не знаете, что вы не можете использовать цикл for вне функции? ?».

1 голос
/ 06 апреля 2016

Ответы, утверждающие, что строки не могут быть проанализированы во время компиляции, неверны. Символьные указатели не могут быть проанализированы во время компиляции, но строковые литералы не являются символьными указателями; это массивы символов, длина которых является частью типа. Это легко забыть, потому что в большинстве случаев гораздо удобнее позволить им распадаться на символ *. Но они не начинают таким образом.

Ах, но как на самом деле определить функцию, которая принимает массив символов фиксированной длины, особенно если бы мы действительно предпочли использовать его для строк произвольной длины? Вот где вывод аргументов шаблона очень удобен:

template<size_t L>
constexpr int hash(const char (&str)[L], int n = L - 1) {
    // hash goes here. You can define recursively as a
    // function of str[n] and hash(str, n-1). Remember to
    // treat 0 as a special case.
}

Это должно начать вас. Очевидно, что сам хэш должен быть достаточно простым для вычислений во время компиляции, но это, вероятно, нормально.

0 голосов
/ 06 сентября 2016

Вот как я делаю этот хэш строки времени компиляции с C ++ 0x:

class StringHash
{
public:
    template <unsigned N, unsigned I>
    struct HashHelper
    {
        constexpr static unsigned Calculate(const char (&str)[N])
        {
            return (HashHelper<N, I - 1>::Calculate(str) ^ (str[I - 1] & 0xFF)) * StringHash::PRIME;
        }
    };

    template <unsigned N>
    struct HashHelper<N, 1>
    {
        constexpr static unsigned Calculate(const char (&str)[N])
        {
            return (StringHash::OFFSET ^ (str[0] & 0xFF)) * StringHash::PRIME;
        }
    };

    template<unsigned N>
    constexpr static unsigned StaticHash(const char (&str)[N])
    {
        return HashHelper<N, N>::Calculate(str);
    }

    static const unsigned OFFSET = 0x01234567;
    static const unsigned PRIME = 0x89ABCDEF;
}

Использование:

static hash = StringHash::StaticHash("Hello"); // You may even use this expression in `switch case`
...