Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to pass string literals as template arguments [duplicate]

I'm trying to find a comfortable way to pass string literals as template arguments. I'm not caring about supporting the widest possible number of compilers, I'm using the latest version of g++ with --std=c++0x.

I've tried a lot of possible solutions but all have disappointed me. I'm sort of giving up, but first I'd like to know why a couple of them failed.

Here they are:

#include <iostream>
#include <string>

using namespace std;

struct String {
    char const *m_sz;

    constexpr String(char const *a_sz)
        :
    m_sz(a_sz) {}

    char const *operator () () const {
        return m_sz;
    }
};

template<class _rstr>
string const Get() {
    return _rstr();
}

int main() {
    cout << Get<String("hello")>() << endl;
    return 0;
}

And:

#include <iostream>
#include <string>

using namespace std;

struct String {
    char const *m_sz;

    constexpr String(char const *a_sz)
        :
    m_sz(a_sz) {}
};

template<String const &_rstr>
string const Get() {
    return _rstr.m_sz;
}

int main() {
    String constexpr str = "hello";
    cout << Get<str>() << endl;
    return 0;
}

The goal was to find a comfortable way to pass a string literal to the useless Get function, which returns its template argument as an std::string object.

EDIT: sorry, maybe my main question isn't clear. My question is: why do those two snippets fail?

like image 241
Alberto La Rocca Avatar asked Aug 03 '13 10:08

Alberto La Rocca


1 Answers

I know the post is old but I haven't found any solution for this problem here, and maybe someone would be interested in my workaround:

template <int N>
constexpr int string_literal_length(const char (&str)[N]) {
   return N - 1;
}

template <int PassedLength, int CountedLength, char... Characters>
struct string_literal {
   static_assert(PassedLength == CountedLength, "Passed to STRING_LITERAL length does not match the length of string...");
};

#define STRING_LITERAL(N, str) string_literal<N, string_literal_length(str), STRING_LITERAL_##N(str)>

// ... as long as we need it ...
#define STRING_LITERAL_128(str) STRING_LITERAL_127(str), str[127]
#define STRING_LITERAL_127(str) STRING_LITERAL_126(str), str[126]
#define STRING_LITERAL_126(str) STRING_LITERAL_125(str), str[125]
#define STRING_LITERAL_125(str) STRING_LITERAL_124(str), str[124]
// ...
#define STRING_LITERAL_5(str) STRING_LITERAL_4(str), str[4]
#define STRING_LITERAL_4(str) STRING_LITERAL_3(str), str[3]
#define STRING_LITERAL_3(str) STRING_LITERAL_2(str), str[2]
#define STRING_LITERAL_2(str) STRING_LITERAL_1(str), str[1]
#define STRING_LITERAL_1(str) str[0]

Now usage:

template <class SLiteral>
struct usage_of_string_literal {
};

int main() {
   usage_of_string_literal<STRING_LITERAL(12, "123456789012")> uosl;
}

Unfortunately one have to provide the length of the string to get it work but it's still more comfortable solution than plain variadic arg template of chars, and the length is verified by the static_assert so the compiler can help to pick appropriate value...


Edit

One more template magic. This one is making use of short-circuit to get rid of the string size from STRING_LITERAL declaration (c++17):

#include <type_traits>
#include <utility>

#define MAX_STRING_LITERAL_LENGTH 11
#define STRING_LITERAL(str) string_literal<char_pack<STRING_LITERAL_11(str)>>::s

#define STRING_LITERAL_11(str) STRING_LITERAL_10(str), ((TERMINATED_10(str))?(str[10]):('\0'))
#define STRING_LITERAL_10(str) STRING_LITERAL_9(str), ((TERMINATED_9(str))?(str[9]):('\0'))
#define STRING_LITERAL_9(str) STRING_LITERAL_8(str), ((TERMINATED_8(str))?(str[8]):('\0'))
#define STRING_LITERAL_8(str) STRING_LITERAL_7(str), ((TERMINATED_7(str))?(str[7]):('\0'))
#define STRING_LITERAL_7(str) STRING_LITERAL_6(str), ((TERMINATED_6(str))?(str[6]):('\0'))
#define STRING_LITERAL_6(str) STRING_LITERAL_5(str), ((TERMINATED_5(str))?(str[5]):('\0'))
#define STRING_LITERAL_5(str) STRING_LITERAL_4(str), ((TERMINATED_4(str))?(str[4]):('\0'))
#define STRING_LITERAL_4(str) STRING_LITERAL_3(str), ((TERMINATED_3(str))?(str[3]):('\0'))
#define STRING_LITERAL_3(str) STRING_LITERAL_2(str), ((TERMINATED_2(str))?(str[2]):('\0'))
#define STRING_LITERAL_2(str) STRING_LITERAL_1(str), ((TERMINATED_1(str))?(str[1]):('\0'))
#define STRING_LITERAL_1(str) str[0]


#define TERMINATED_10(str) TERMINATED_9(str) && str[9]
#define TERMINATED_9(str) TERMINATED_8(str) && str[8]
#define TERMINATED_8(str) TERMINATED_7(str) && str[7]
#define TERMINATED_7(str) TERMINATED_6(str) && str[6]
#define TERMINATED_6(str) TERMINATED_5(str) && str[5]
#define TERMINATED_5(str) TERMINATED_4(str) && str[4]
#define TERMINATED_4(str) TERMINATED_3(str) && str[3]
#define TERMINATED_3(str) TERMINATED_2(str) && str[2]
#define TERMINATED_2(str) TERMINATED_1(str) && str[1]
#define TERMINATED_1(str) str[0]

template <char... Cs>
struct char_pack {
    static constexpr char const arr[sizeof...(Cs) + 1] = {Cs..., 0};
    static constexpr std::size_t non_zero_count = (((Cs != 0)?1:0) + ...);
    static_assert(non_zero_count < MAX_STRING_LITERAL_LENGTH, "You need to create more macros");
};

template <char... Cs>
constexpr char const char_pack<Cs...>::arr[sizeof...(Cs) + 1];

template <char... Cs>
constexpr std::size_t char_pack<Cs...>::non_zero_count;

template <class CP, class = void, class = std::make_index_sequence<CP::non_zero_count>>
struct string_literal;

template <char... Cs, std::size_t... Is>
struct string_literal<char_pack<Cs...>, std::enable_if_t<(Cs && ...)>, std::index_sequence<Is...>> {
    static constexpr char const s[sizeof...(Cs) + 1] = {Cs..., '\0'};
};

template <char... Cs, std::size_t... Is> 
constexpr char const string_literal<char_pack<Cs...>, std::enable_if_t<(Cs && ...)>, std::index_sequence<Is...>>::s[sizeof...(Cs) + 1];

template <char... Cs, std::size_t... Is>
struct string_literal<char_pack<Cs...>, std::enable_if_t<!(Cs && ...)>, std::index_sequence<Is...>>: string_literal<char_pack<char_pack<Cs...>::arr[Is]...>> { };

template <const char *>
struct foo {};

int main() {
    foo<STRING_LITERAL("abcdefghij")> f;
    static_cast<void>(f);
}

[live demo]

like image 146
W.F. Avatar answered Sep 30 '22 17:09

W.F.