Frage

In C++, a constant-time lookup for integers to variable values can be generated at compile time in the following way:

template<int>
int *map() {
    static int var = 0;
    return &var;
}

int main() {
    *map<0>() = 42;
    *map<1>() = 1337;

    return *map<0>(); //returns 42
}

Note that the compiler will create a global variable map<key>::var for every 'key' that is used at compile-time.

Is it possible to create a similar map function that uses character literals as 'keys'? Note that character literals can not be used as template arguments, due to their local linkage.

I need to be able to specify new keys in any part of my code, and, in fact, as part of any expression. Note how in my integer example I specify that map<0> should exist only in main().

Note: In particular, I want to use the tuple of __FILE__, __LINE__ as the key, make the map thread-specific by prefixing var with thread_local, and translation-unit specific by prefixing map() with static. Thus, in theory, the local linkage of character literals wouldn't pose a problem. The whole thing is a performance optimization for a logger, which allows specifying loglevels for sections of specific files.

War es hilfreich?

Lösung 2

You may transform your literal string into custom type.
Following may help: (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'>

Andere Tipps

Here a proof of concept, even if i do not recommend it in that implementation, note that this use c++1y for ease on 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;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top