5

I'm trying to convert a C++ literal string into an instance of the following template:

template <char ... C>
struct string_literal {
    typedef string_constant type;
    static constexpr const char value[sizeof...(C)] = {C...};
    constexpr operator const char* (void) const {
        return value;
    }
};
template <char ... C>
constexpr const char string_literal<C...>::value[sizeof...(C)];

I came up with these helpers based on various sources for 'unpacking' the quoted string value into the template above.

template <unsigned N, const char (&S) [N], typename U>
struct selector;

template <unsigned N, const char (&S) [N], unsigned ...I>
struct selector<N, S, index_sequence<I...>> {
    using type = string_literal<S[I]...>;
};

template <unsigned N, const char (&S) [N]>
struct unpack {
    using type = typename selector<N, S, make_index_sequence<N>>::type;
};

However, when a call this I get a compiler error:

template <unsigned N>
constexpr auto make_string_literal(const char (&s) [N]) {
    return unpack<N, s>{}; // Error here
}

constexpr auto literal = make_string_literal("test");
// string_literal<'t','e','s','t','\0'>

GCC 4.9+ reports: error: 'const char (& s)[1]' is not a valid template argument for type 'const char (&)[1]' because a reference variable does not have a constant address

Clang 3.7.1 reports: error: non-type template argument refers to object 's' that does not have linkage

I tried a few different approaches but the errors are mostly the same. What am I missing here?

Claudio
  • 10,614
  • 4
  • 31
  • 71
  • 1
    You can use GNU extension ([example](https://github.com/tomilov/parser_generator/blob/master/src/main.cpp#L29)). It is the only way thinkable currently. – Tomilov Anatoliy Feb 27 '16 at 14:59
  • Alternate -unsatisfactory- solution that compiles under gcc and clang: http://ideone.com/uKP2qj – Ricardo Andrade Feb 29 '16 at 05:56
  • `(void)` is C-izm. `operator const char *` is currently wrong (error-prone): you may need to change definiton of `value` from `value[sizeof...(C)] = {C...};` to `value[sizeof...(C) + 1] = {C..., '\0'};`, because currently its correctness depends on way of acquisition of source char array. – Tomilov Anatoliy Mar 01 '16 at 06:18
  • Just to be clear @Orient. The deal of the sizeof+1 is to be sure that there's a null terminator for `operator const char*` even when the template is instantiated directly (i.e. `<'a', 'b', 'c'>`) instead of an initialization from a literal string that already has the null terminator. Am I right? – Ricardo Andrade Mar 02 '16 at 05:01
  • Yes. I think so. Also `char a[3] = {'1', '2', '3'};` is possilbe case. – Tomilov Anatoliy Mar 02 '16 at 06:03
  • Your approach cannot work, because non-type template arguments are explicitly restricted [to not permit string literals](http://eel.is/c++draft/temp.arg.nontype#1). – Columbo Mar 22 '16 at 10:21
  • This is a dup of http://stackoverflow.com/q/35098061/3647361 – Columbo Mar 22 '16 at 10:27
  • It works if you remove all “_create” parts, ie Delete this whole fragment: > template > constexpr string_literal operator ""_create() > { > return {}; > } and convert this (in main function) string_literal<'t','e','s','t'> s = "test"_create; To this > string_literal<'t','e','s','t'> s;` This is just a comment/reply to the answer of prestokeys – Karl Jul 24 '20 at 16:16

1 Answers1

2

Is this a satisfactory solution to meet your needs?

template <char ... C>
struct string_literal {
    static constexpr const char value[sizeof...(C)] = {C...};
    constexpr operator const char* (void) const {
        return value;
    }
    void foo() {std::cout << value << '\n';}
};
template <char ... C> constexpr const char string_literal<C...>::value[sizeof...(C)];

template <typename CharT, CharT... Cs>
constexpr string_literal<Cs...> operator ""_create() {
    return {};
}

int main() {
    string_literal<'t','e','s','t'> s = "test"_create;
    std::cout << s << '\n';  // test
    s.foo();  // test
}
prestokeys
  • 4,817
  • 3
  • 20
  • 43
  • 2
    According to the comments of others, this depends on a GNU extension (constexpr user-defined literals) which is neither part of the current or the next C++ standard (so far): http://cplusplus.github.io/EWG/ewg-active.html#66 – Ricardo Andrade Apr 06 '16 at 22:07