【问题标题】:c++11: Constant-time lookup function for string literalsc++11:字符串文字的常量时间查找函数
【发布时间】:2014-03-29 16:18:12
【问题描述】:

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&lt;key&gt;::var

是否可以创建一个使用字符文字作为“键”的类似映射函数?请注意,由于它们的本地链接,字符文字不能用作模板参数。

我需要能够在我的代码的任何部分指定新键,事实上,作为任何表达式的一部分。请注意,在我的整数示例中,我如何指定 map&lt;0&gt; 应仅存在于 main() 中。

注意:特别是,我想使用 __FILE__, __LINE__ 的元组作为键,通过在 var 前加上 thread_local 使映射线程特定,并通过在 map() 前加上 @ 使翻译单元特定987654332@。因此,理论上,字符文字的本地链接不会造成问题。整个事情是对记录器的性能优化,它允许为特定文件的部分指定日志级别。

【问题讨论】:

  • constexpr 不会为此工作吗? (除非您需要在运行时设置地图的条目。)
  • 看起来有点像 XY 问题。你能解释一下你真正想要做什么吗?
  • 也许把你的字符文字转换成mpl::string这样的类型。
  • 我想不惜一切代价避免提升。此外,由于我想使用__FILE__,它需要使用字符串文字。除此之外,如果可能的话,我还想避免在我的符号中使用无穷无尽的整数列表。还有,哇。这 15 行测试代码只用了 4 秒就在 g++ 中构建。

标签: c++ templates c++11 template-meta-programming


【解决方案1】:

这里是一个概念证明,即使我不建议在 implementation 中使用它,请注意这使用 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;
}

【讨论】:

  • 您的回答表明我的问题存在缺陷:我需要能够在代码中的任何位置指定新键,作为任何表达式的一部分。使用您的代码,需要在 str_to_val 中指定所有键。因此,不接受,而是 +1。
【解决方案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'>

【讨论】:

  • 第一个视图看起来相当不错,在这里查看我的完整示例实现:pastebin.com/841qvQBp 是否有机会避免限制最大文字长度的宏定义的对数数量?
  • 不幸的是,我不知道有什么方法可以避免这种情况:(
  • @mic_e:有一个 gnu 扩展:template&lt;class CharT, CharT... cs&gt; char_sequence&lt;cs...&gt; operator ""_seq(){ return {};}
猜你喜欢
  • 2012-11-23
  • 1970-01-01
  • 2016-07-08
  • 2020-05-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-15
  • 1970-01-01
相关资源
最近更新 更多