C++11: функция поиска с постоянным временем для строковых литералов
В C++
во время компиляции поиск целых чисел в значениях переменной можно производить следующим образом:
template<int>
int *map() {
static int var = 0;
return &var;
}
int main() {
*map<0>() = 42;
*map<1>() = 1337;
return *map<0>(); //returns 42
}
Обратите внимание, что компилятор создаст глобальную переменную map<key>::var
для каждого "ключа", который используется во время компиляции.
Можно ли создать аналогичную функцию карты, которая использует литералы символов в качестве "ключей"? Обратите внимание, что символьные литералы не могут использоваться в качестве аргументов шаблона из-за их локальной связи.
Мне нужно иметь возможность указывать новые ключи в любой части моего кода и фактически как часть любого выражения. Обратите внимание, как в моем целочисленном примере я указываю, что map<0>
должен существовать только в main()
,
Примечание: в частности, я хочу использовать кортеж __FILE__, __LINE__
в качестве ключа сделайте карту специфичной для потока, добавив префикс var
с thread_local
и специфичный для единицы перевода перевод с префиксом map()
с static
, Таким образом, теоретически, локальная связь символьных литералов не представляет проблемы. Все дело в оптимизации производительности логгера, которая позволяет указывать уровни логирования для разделов определенных файлов.
2 ответа
Вы можете преобразовать свою литеральную строку в пользовательский тип.
Может помочь следующее: ( https://ideone.com/DPQQyD)
#include <cstdint>
// Sequence of char
template <char... Cs> struct char_sequence
{
template <char C> using push_back = char_sequence<Cs..., C>;
};
// Remove all chars from char_sequence from '\0'
template <typename, char...> struct strip_sequence;
template <char... Cs>
struct strip_sequence<char_sequence<>, Cs...>
{
using type = char_sequence<Cs...>;
};
template <char...Cs, char...Cs2>
struct strip_sequence<char_sequence<'\0', Cs...>, Cs2...>
{
using type = char_sequence<Cs2...>;
};
template <char... Cs, char C, char... Cs2>
struct strip_sequence<char_sequence<C, Cs...>, Cs2...>
{
using type = typename strip_sequence<char_sequence<Cs...>, Cs2..., C>::type;
};
// helper to get the i_th character (`\0` for out of bound)
template <std::size_t I, std::size_t N>
constexpr char at(const char (&a)[N]) { return I < N ? a[I] : '\0'; }
// helper to check if the c-string will not be truncated
template <std::size_t max_size, std::size_t N>
constexpr bool check_size(const char (&)[N])
{
static_assert(N <= max_size, "string too long");
return N <= max_size;
}
// Helper macros to build char_sequence from c-string
#define PUSH_BACK_8(S, I) \
::push_back<at<(I) + 0>(S)>::push_back<at<(I) + 1>(S)> \
::push_back<at<(I) + 2>(S)>::push_back<at<(I) + 3>(S)> \
::push_back<at<(I) + 4>(S)>::push_back<at<(I) + 5>(S)> \
::push_back<at<(I) + 6>(S)>::push_back<at<(I) + 7>(S)>
#define PUSH_BACK_32(S, I) \
PUSH_BACK_8(S, (I) + 0) PUSH_BACK_8(S, (I) + 8) \
PUSH_BACK_8(S, (I) + 16) PUSH_BACK_8(S, (I) + 24)
#define PUSH_BACK_128(S, I) \
PUSH_BACK_32(S, (I) + 0) PUSH_BACK_32(S, (I) + 32) \
PUSH_BACK_32(S, (I) + 64) PUSH_BACK_32(S, (I) + 96)
// Macro to create char_sequence from c-string (limited to 128 chars)
#define MAKE_CHAR_SEQUENCE(S) \
strip_sequence<char_sequence<> \
PUSH_BACK_128(S, 0) \
>::type::template push_back<check_size<128>(S) ? '\0' : '\0'>
Вот подтверждение концепции, даже если я не рекомендую ее в этой реализации, обратите внимание, что это использует C++1y для простоты на constexpr:
#include <iostream>
constexpr bool cstrcmp( char const * s1, char const * s2 ) {
while ( *s2 && *s1 ) {
if (*s1++ != *s2++ )
return false;
}
return !*s1 && !*s2;
}
constexpr int str_to_val( char const * str ) {
struct pair { char const*str; int value; };
constexpr pair const tab[] { {"foo",1}, {"bar", 2} };
for( auto & e : tab ) {
if ( cstrcmp(str,e.str) )
return e.value;
}
throw 0;
}
int main() {
constexpr auto test_0 = str_to_val("foo");
constexpr auto test_1 = str_to_val("bar");
//constexpr auto test_2 = str_to_val("baz"); // trigger compilation error
std::cout << test_0 << " " << test_1 << std::endl;
}