2016-09-22 2 views
13

Допустим, что у вас есть функция, которая генерирует определенный токен безопасности для вашего приложения, например, некоторую соль хешей, или, может быть, симметричный или асимметричный ключ.Как обеспечить, чтобы функция constexpr никогда не вызывалась во время выполнения?

Теперь скажем, что у вас есть эта функция в C++ как constexpr и что вы создаете ключи для своей сборки на основе некоторой информации (например, номер сборки, временная метка, что-то еще).

Вы, будучи добросовестным программистом, убедитесь в этом и позвоните соответствующим образом, чтобы убедиться, что он вызван только во время компиляции, и, таким образом, мертвый стриппер удаляет код из окончательного исполняемого файла.

Однако вы никогда не можете быть уверены, что кто-то другой не будет называть его небезопасным способом, или что, возможно, компилятор не отключит функцию, а затем ваш алгоритм маркера безопасности станет общедоступным знания, облегчающие его, были бы злоумышленниками, чтобы угадать будущие жетоны.

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

Есть ли способ гарантировать, что функция constexpr никогда не будет вызвана во время выполнения? Или поочередно, бросать утверждение или подобное во время выполнения было бы хорошо, но не так идеально, как ошибка компиляции.

Я слышал, что существует какой-то способ включения типа исключения, которого не существует, так что если функция constexpr не будет отключена, вы получите ошибку компоновщика, но слышали, что это работает только на некоторых компиляторах.

Отдаленно связанный с этим вопрос: Force constexpr to be evaluated at compile time

+0

Одно из возможных решений: вы реализуете эту функцию строго в терминах 'template <...> struct xyz {static constexpr long long value = ...; } '. Нет, действительно, я имею в виду, что не использую 'constexpr function', а реализую вычисления строго в шаблонах структуры. –

+3

Обратите внимание, что в целом соглашается, что, зная ваш алгоритм, достаточно, чтобы он был сломан, тогда ваш алгоритм - дерьмо. – immibis

+0

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

ответ

11

Вы можете заставить использовать его в постоянном выражении:

#include<utility> 

template<typename T, T V> 
constexpr auto ct() { return V; } 

template<typename T> 
constexpr auto func() { 
    return ct<decltype(std::declval<T>().value()), T{}.value()>(); 
} 

template<typename T> 
struct S { 
    constexpr S() {} 
    constexpr T value() { return T{}; } 
}; 

template<typename T> 
struct U { 
    U() {} 
    T value() { return T{}; } 
}; 

int main() { 
    func<S<int>>(); 
    // won't work 
    //func<U<int>>(); 
} 

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

+1

Это хороший трюк! Это полезно, но знаете ли вы, есть ли способ сделать это, чтобы функция не вызывалась во время выполнения? –

+1

@AlanWolfe Таким образом, он не может быть вызван во время выполнения, вы получите сообщение об ошибке во время компиляции. Разумеется, вам просто нужно скрыть детали за прекрасно определенным интерфейсом. – skypjack

+2

ct может быть заменен на std :: integral_constant в C++ 11. См. Http://en.cppreference.com/w/cpp/types/integral_constant – mabraham

5

теоретическое решение (как шаблоны должны быть Тьюринга) - не использовать функции constexpr и падают обратно на старую-добрую std=c++0x стиль вычисления с использованием исключительно struct template with values. Например, не делайте

constexpr uintmax_t fact(uint n) { 
    return n>1 ? n*fact(n-1) : (n==1 ? 1 : 0); 
} 

но

template <uint N> struct fact { 
    uintmax_t value=N*fact<N-1>::value; 
} 
template <> struct fact<1> 
    uintmax_t value=1; 
} 
template <> struct fact<0> 
    uintmax_t value=0; 
} 

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

Тот факт, что ребята из boost смогли сделать compile time parser, является сильным сигналом, который, хотя и утомительный, должен быть осуществимым - это разовая стоимость, может быть, можно считать его инвестицией.


Например:

к мощности структуры:

// ***Warning: note the unusual order of (power, base) for the parameters 
// *** due to the default val for the base 
template <unsigned long exponent, std::uintmax_t base=10> 
struct pow_struct 
{ 
private: 
    static constexpr uintmax_t at_half_pow=pow_struct<exponent/2, base>::value; 
public: 
    static constexpr uintmax_t value= 
     at_half_pow*at_half_pow*(exponent % 2 ? base : 1) 
    ; 
}; 

// not necessary, but will cut the recursion one step 
template <std::uintmax_t base> 
struct pow_struct<1, base> 
{ 
    static constexpr uintmax_t value=base; 
}; 


template <std::uintmax_t base> 
struct pow_struct<0,base> 
{ 
    static constexpr uintmax_t value=1; 
}; 

Билд лексема

template <uint vmajor, uint vminor, uint build> 
struct build_token { 
    constexpr uintmax_t value= 
     vmajor*pow_struct<9>::value 
    + vminor*pow_struct<6>::value 
    + build_number 
    ; 
} 
+2

a. '0! = 1'. б. Специализация для '1!' Избыточна. – SomeWittyUsername

+0

@SomeWittyUsername Хорошо, тогда мой 'fact' фактически не вычисляет математическое выражение factorial. Это все еще верный пример для этой техники, не так ли? –

+0

Надеюсь, есть другие варианты (сейчас или в будущем с constexpr), но спасибо за ответ! –

Смежные вопросы