I need a class which takes in a compile-time string literal (const char* const) as its only constructor argument.
I would like to have an std::array member variable store the words in this string literal (split up using spaces, for example). The size of this array therefore will never change, as the string literal is known at compile-time.
How can I have this array as a member variable, while having its size be specified by the number of words passed in to the constructor?
I would like to make it so that the caller does not need to specify the number of words in the class template parameters, but that the constructor (presumably a constexpr one) could deduce this at compile time.
I do not want to use a vector, as it is unnecessary for this situation (the words being stored will NEVER change). How would I go about this?
Thanks
First, you have to use class templates, as there should be a different size of the class for different string literals. Template arguments should at least contain the size of the enclosed array (e.g. template<std::size_t N>).
Then you may find Class Template Argument Deduction from C++17 useful so you don't have to specify size automatically. You can either use automatic deduction from constructor or provide custom deduction guides. If you don't have C++17, you can create a helper function similar to make_pair/make_tuple which will employ template argument deduction and create the corresponding class.
#include <algorithm>
#include <array>
#include <cstddef>
#include <iterator>
#include <iostream>
template<std::size_t N>
struct WordStorage {
std::array<char, N> char_array;
WordStorage(const char (&char_array_)[N]) {
std::copy(char_array_, char_array_ + N, char_array.begin());
}
};
int main() {
WordStorage s("hello"); // Deduced to WordStorage<6> as "hello" is const char[6].
static_assert(s.char_array.size() == 6);
std::cout << s.char_array.data() << "\n";
}
probably closest thing you can get in my opinion :
template <char... c>
class foo
{
private:
std::array<char, sizeof...(c)> m_arr;
public:
foo()
{
std::cout << m_arr.size()<<'\n'; //ok : prints 3
}
};
int main()
{
foo<'c', 'h', 'a'> f;
}
I am currently writing some template code where the template parameter is the char type to use. This causes a problem when referring to literal strings. I can, of course, make a struct with the strings I use but I was thinking if it would be possible to make something like:
template<typename chartype, char32_t... chars>
struct tr {
/* some magic here */
};
so that tr<char32_t,U"hello world"> would result in U"hello world"
and tr<char16_t,U"Hello world"> would result in u"hello world"
and tr<char,U"hello world"> would result in "hello world" (in UTF-8).
The magic should of course correctly translate codes above 0x10000 to lead code and follow code for char16_t and to proper 2, 3 and 4 byte codes for UTF-8
at compile time.
Problem is: how do you define a constant C-style string of a given char type using the char32_t... chars template argument? You can extract the characters
from it but how do you rebuild a new string based on the chars of the input string in template code?
Note, the preprocessor can correctly define a string such as "hello world" with suitable prefix u or U if you like but it cannot access the individual characters of the string to properly translate it.
EDIT:
Strings as template arguments are definitely possible in new C++, however,
the template argument is NOT declared as const char * or something like that:
template <char... txt>
struct foo { ... }
allows you to write foo<"hello"> as a type with the string "hello" as template argument. The problem is how to build the string from those characters.
I mean at some point you want the struct to contain a string value to return:
template <char32_t... txt>
struct foo;
template <>
struct foo<> {
static const char16_t * txt() { return ""; }
};
template <char32_t a, char32_t... rest>
struct foo<a, rest...> {
static const char16_t * txt()
{
char16_t buf[100];
int k = 0;
if (a < 0x10000) buf[k++] = a;
else {
buf[k++] = 0xd800 | ((a - 0x10000) >> 10);
buf[k++] = 0xdc00 | ((a-0x10000) & 0x3ff);
}
// copy rest of string into buf + 2..99
u16strcpy(buf + k, foo<rest...>::txt());
return buf;
}
}
Several obvious problems with this "solution", one problem is that buf only have room for 100 characters which will fail if the string is longer. but the main problem is that I wanted this to happen in compile time and this looks very much like run time code to me and is not at all what I wanted to do.
Basically I wanted something that works this way:
foo<char, "hello"> results in something that is effectively a string literal
"hello" or u8"hello".
foo<char16_t, "hello"> results in something that is effectively a string literal u"hello" and foo<char32_t, "hello"> results in something that is effectively a string literal U"hello".
The problem is when writing template code to handle various character formats and then having string literals involved. Yes, you can write a simple struct:
template <typename ChT> struct bar;
template <>
struct bar<char32_t> {
static const char32_t * txta = U"AAAA";
static const char32_t * txtb = U"BBBB";
};
and so on and bar<char16_t> has txta = u"AAAA" etc. Then refer to the strings
in your templated code by bar<T>::txta etc. However, I wish there was a way that you could specify those strings directly in templated code and the compiler would do the right thing. A templated string literal in other words.
Maybe it should be added as a feature to the language that
T<char32_t> string-literal is the same as U string-literal etc
so that you could write
template <typename ChT>
struct foo {
static const ChT * txta = T<ChT> "AAAAA";
};
and the compiler would do the right thing.
This would appear to simply not be legal, even the following is rejected (vs2017, with standard set to latest):
template<char const * ptr>
struct test
{};
void bar()
{
test<"testing"> t;
}
with the error: invalid expression as a template argument for 'ptr', and if that's not going to work trying to convert it at compile-time is a non-starter. And honestly this doesn't seem all that surprising that a pointer-to-data isn't constant enough. to be a template argument.
Here are some tools to make it work in C++17 (might be portable to C++11 and C++14):
static constexpr data member of templated class
The output literal you wish to work with needs some "storage". I suggest to instantiate a unique class template for each literal, e.g., Literal<char, 'f', 'o', 'o', '\0'>. That class can hold the data as astatic constexpr` member.
template<class C, C... cs>
struct Literal {
static_assert(sizeof...(cs) >= 1);
static constexpr C data[] = {cs...};// or use `std::array<C, sizeof...(cs)>`
};
template<class C, C... cs>
constexpr C Literal<C, cs...>::data[];
user-defined string literal to simplify syntax
Of course you wish to avoid typing, e.g., Literal<char, 'f', 'o', 'o', '\0'>. A useful tool to achieve that is the following overload for user-defined string literals.
template<class C, C... cs>
constexpr Literal<C, cs..., C('\0')> operator""_c() {// or use `auto`
return Literal<C, cs..., C('\0')>{};
}
Note how the input literal is passed as non-type template parameters to that overload. That way, it is possible to "carry the value as a type".
constexpr algorithms for re-encoding
So far, you can type "foo"_c to obtain a Literal<char, 'f', 'o', 'o', '\0'> which has a static constexpr data member yielding the same as "foo". Next you can pass that Literal<char, 'f', 'o', 'o', '\0'> to a function which returns a const char16_t(&)[4] to data of the corresponding Literal<char16_t, ..., '\0'>. The syntax could read tr<char16_t>("foo"_c).
The code that transforms a Literal<char, ...> into the corresponding Literal<char16_t, ...> may be based on constexpr algorithms as shown below.
template<
class OutChar, class InChar, InChar... cs,
std::size_t... input_indices, std::size_t... output_indices
>
constexpr auto& tr_impl(// called by `tr` with appropriate `index_sequence`s
Literal<InChar, cs...>,
std::index_sequence<input_indices...>,
std::index_sequence<output_indices...>
) {
constexpr std::size_t outsize = sizeof...(output_indices);
using Buffer = std::array<OutChar, outsize>;
constexpr Buffer buf = encode_as<OutChar, outsize>({cs...});
return Literal<OutChar, buf[output_indices]...>::data;
}
template<class OutChar, class InChar, InChar... cs>
constexpr auto& tr(Literal<InChar, cs...> literal) {
constexpr std::size_t outsize = count_as<OutChar>({cs...});
return tr_impl<OutChar>(
literal,
std::make_index_sequence<sizeof...(cs)>{},// input indices
std::make_index_sequence<outsize>{}// output indices
);
}
The remaining part would be to implement those functions count_as and encode_as.
assign to constexpr auto& in final usage
Finally you can assign to constexpr auto& to verify that type and value are equivalent to the plain string literal based on the desired character type.
static_assert(std::size(U"𝄞𝄞") == 3);
static_assert(std::size(u"𝄞𝄞") == 5);
constexpr auto& test = tr<char16_t>(U"𝄞𝄞"_c);
static_assert(std::is_same<decltype(test), const char16_t(&)[5]>{});
for(std::size_t i=0; i<std::size(u"𝄞𝄞"); ++i) {
assert(test[i] == u"𝄞𝄞"[i]);
std::cout << i << ": " << test[i] << std::endl;
}
I was suprised when I discovered that it's possible to make user-defined literals templated:
template <char ...C> std::string operator ""_s()
{
char arr[]{C...};
return arr;
}
// ...
std::cout << 123_s;
But above declaration does not work with string literals:
"123"_s
gives me following error:
prog.cpp: In function 'int main()':
prog.cpp:12:15: error: no matching function for call to 'operator""_s()'
std::cout << "123"_s;
prog.cpp:4:34: note: candidate: template std::string operator""_s()
template std::string operator ""_s()
prog.cpp:4:34: note: template argument deduction/substitution failed:
(Ideone)
Is there is a way to use templated user-defined literals with string literals as well?
Clang and GCC support an extension that allows you to do
template<class CharT, CharT... Cs>
std::string operator ""_s() { return {Cs...}; }
But there is nothing in standard C++; proposals to standardize this have been made several times and rejected each time, most recently less than a month ago, mostly because a template parameter pack is a really inefficient way to represent strings.
Pre-C++20 it's impossible in standard C++. Refer to #T.C.'s answer for a non-standard solution that works with GCC and Clang.
Starting with C++20, you can pass string literals as template parameters. You need a helper class like this one:
#include <algorithm>
#include <cstddef>
#include <string_view>
namespace impl
{
// Does nothing, but causes an error if called from a `consteval` function.
inline void ExpectedNullTerminatedArray() {}
}
// A string that can be used as a template parameter.
template <std::size_t N>
struct ConstString
{
char str[N]{};
static constexpr std::size_t size = N - 1;
[[nodiscard]] std::string_view view() const
{
return {str, str + size};
}
consteval ConstString() {}
consteval ConstString(const char (&new_str)[N])
{
if (new_str[N-1] != '\0')
impl::ExpectedNullTerminatedArray();
std::copy_n(new_str, size, str);
}
};
It allows you to do this:
#include <iostream>
template <ConstString S>
void Print()
{
std::cout << S.view() << '\n';
}
int main()
{
Print<"foo">();
}
This might be enough for your use case, and you might not need template UDLs per se.
But if you do need them, it's possible:
#include <iostream>
template <ConstString S>
void operator""_print()
{
std::cout << S.view();
}
int main()
{
"foo"_print;
}
So, I'm wishing to be able to have a static const compile
time struct that holds some value based on a string by using
templates. I only desire up to four characters. I know that
the type of 'abcd' is int, and so is 'ab','abc', and although
'a' is of type char, it works out for a template<int v> struct
What I wish to do is take sizes of 2,3,4,5 of some const char, "abcd"
and have the same functionality as if they used 'abcd'. Note that
I do not mean 1,2,3, or 4 because I expect the null terminator.
cout << typeid("abcd").name() << endl; tells me that the type for
this hard coded string is char const [5], which includes the null
terminator on the end.
I understand that I will need to twiddle the values as characters,
so they are represented as an integer.
I cannot use constexpr since VS10 does not support it (VS11 doesn't either..)
So, for example with somewhere this template defined, and later the last line
template <int v> struct something {
static const int value = v;
};
//Eventually in some method
cout << typeid(something<'abcd'>::value).name() << endl;
works just fine.
I've tried
template<char v[5]> struct something2 {
static const int value = v[0];
}
template<char const v[5]> struct something2 {
static const int value = v[0];
}
template<const char v[5]> struct something2 {
static const int value = v[0];
}
All of them build individually, though when I throw in my test,
cout << typeid(something2<"abcd">::value).name() << endl;
I get
'something2' : invalid expression as a template argument for 'v'
'something2' : use of class template requires template argument list
Is this not feasible or am I misunderstanding something?
14.1 lists the acceptable types of non-type template arguments:
— integral or enumeration type,
— pointer to object or pointer to function,
— lvalue reference to object or lvalue reference to function,
— pointer to member,
Arrays don't fit under any of these categories.
14.3.2/1 lists categories of what's permitted as template arguments and 14.3.2/2 goes on to say:
Note: A string literal (2.14.5) does not satisfy the requirements of any of these categories and thus is not an acceptable template-argument.
Therefore you cannot do what you're trying to do.
you cannot, from the standard.
14.3.2.1:
A template-argument for a non-type, non-template template-parameter shall be one of:
an integral constant-expression of integral or enumeration type; or
the name of a non-type template-parameter; or
the address of an object or function with external linkage, including function templates >and function template-ids but excluding non-static class members, expressed as & id->expression where the & is optional if the name refers to a function or array, or if the >corresponding template-parameter is a reference; or
a pointer to member expressed as described in 5.3.1 .
There is a way to get close to what you are wanting. Possibly this will work for you, although it adds an extra level of code to be maintained.
It requires defining const char arrays with external linkage, then using the names of those arrays to instantiate classes from the templates. Of course in real use this code would be separated into various .h and .cpp files.
extern const char a[] = "a";
extern const char b[] = "b";
extern const char ab[] = "ab";
extern const char abc[] = "abc";
template <const char * const T> class Test
{
public:
Test() {str = typename T;};
private:
const char * str;
};
SomeFunction()
{
Test<a> A;
Test<b> B;
Test<ab> AB;
Test<abc> ABC;
}
I want to declare a class template in which one of the template parameters takes a string literal, e.g. my_class<"string">.
Can anyone give me some compilable code which declares a simple class template as described?
Note: The previous wording of this question was rather ambiguous as to what the asker was actually trying to accomplish, and should probably have been closed as insufficiently clear. However, since then this question became multiple times referred-to as the canonical ‘string literal type parameter’ question. As such, it has been re-worded to agree with that premise.
You can have a const char* non-type template parameter, and pass it a const char[] variable with static linkage, which is not all that far from passing a string literal directly.
#include <iostream>
template<const char *str>
struct cts {
void p() {std::cout << str;}
};
static const char teststr[] = "Hello world!";
int main() {
cts<teststr> o;
o.p();
}
http://coliru.stacked-crooked.com/a/64cd254136dd0272
Further from Neil's answer: one way to using strings with templates as you want is to define a traits class and define the string as a trait of the type.
#include <iostream>
template <class T>
struct MyTypeTraits
{
static const char* name;
};
template <class T>
const char* MyTypeTraits<T>::name = "Hello";
template <>
struct MyTypeTraits<int>
{
static const char* name;
};
const char* MyTypeTraits<int>::name = "Hello int";
template <class T>
class MyTemplateClass
{
public:
void print() {
std::cout << "My name is: " << MyTypeTraits<T>::name << std::endl;
}
};
int main()
{
MyTemplateClass<int>().print();
MyTemplateClass<char>().print();
}
prints
My name is: Hello int
My name is: Hello
C++20 fixed_string + "Class Types in Non-Type Template Parameters"
Apparently, a proposal for this was first accepted, but then removed: "String literals as non-type template parameters"
The removal was partly because it was deemed to be easy enough to do with another proposal that was accepted: "Class Types in Non-Type Template Parameters".
The accepted proposal contains an example with the following syntax:
template <std::basic_fixed_string Str>
struct A {};
using hello_A = A<"hello">;
I'll try to update this with an example that actually tells me anything once I see a compiler that supports it.
A Redditor has also shown that the following compiles on GCC master, provided you define your own version of basic_fixed_string which was not in the standard library yet: https://godbolt.org/z/L0J2K2
template<unsigned N>
struct FixedString {
char buf[N + 1]{};
constexpr FixedString(char const* s) {
for (unsigned i = 0; i != N; ++i) buf[i] = s[i];
}
constexpr operator char const*() const { return buf; }
};
template<unsigned N> FixedString(char const (&)[N]) -> FixedString<N - 1>;
template<FixedString T>
class Foo {
static constexpr char const* Name = T;
public:
void hello() const;
};
int main() {
Foo<"Hello!"> foo;
foo.hello();
}
g++ -std=c++2a 9.2.1 from the Ubuntu PPA fails to compile that with:
/tmp/ccZPAqRi.o: In function `main':
main.cpp:(.text+0x1f): undefined reference to `_ZNK3FooIXtl11FixedStringILj6EEtlA7_cLc72ELc101ELc108ELc108ELc111ELc33EEEEE5helloEv'
collect2: error: ld returned 1 exit status
Bibliography: https://botondballo.wordpress.com/2018/03/28/trip-report-c-standards-meeting-in-jacksonville-march-2018/
Finally, EWG decided to pull the previously-approved proposal to allow string literals in non-type template parameters, because the more general facility to allow class types in non-type template parameters (which was just approved) is a good enough replacement. (This is a change from the last meeting, when it seemed like we would want both.) The main difference is that you now have to wrap your character array into a struct (think fixed_string or similar), and use that as your template parameter type. (The user-defined literal part of P0424 is still going forward, with a corresponding adjustment to the allowed template parameter types.)
This will be especially cool with the C++17 if constexpr: if / else at compile time in C++?
This kind of feature appears to be in line with the awesome "constexpr everything" proposals that went into C++20, such as: Is it possible to use std::string in a constexpr?
Sorry, C++ does not currently support the use of string literals (or real literals) as template parameters.
But re-reading your question, is that what you are asking? You cannot say:
foo <"bar"> x;
but you can say
template <typename T>
struct foo {
foo( T t ) {}
};
foo <const char *> f( "bar" );
This is a solution with MPLLIBS to pass a strings as template arguments ( C++11 ).
#include <iostream>
#include <mpllibs/metaparse/string.hpp> // https://github.com/sabel83/mpllibs
#include <boost/mpl/string.hpp>
// -std=c++11
template<class a_mpl_string>
struct A
{
static const char* string;
};
template<class a_mpl_string>
const char* A< a_mpl_string >
::string { boost::mpl::c_str< a_mpl_string >::value }; // boost compatible
typedef A< MPLLIBS_STRING ( "any string as template argument" ) > a_string_type;
int main ( int argc, char **argv )
{
std::cout << a_string_type{}.string << std::endl;
return 0;
}
prints:
any string as template argument
The lib on github: https://github.com/sabel83/mpllibs
inline const wchar_t *GetTheStringYouWant() { return L"The String You Want"; }
template <const wchar_t *GetLiteralFunc(void)>
class MyType
{
void test()
{
std::cout << GetLiteralFunc;
}
}
int main()
{
MyType<GetTheStringYouWant>.test();
}
Try it with pasing the address of a function as the template argument.
EDIT: ok the title of your question seems to be misleading
"I want a class which takes two parameters in its constructor. The first can be either an int, double or float, so , and the second is always a string literal "my string", so I guess const char * const."
It looks like you're trying to achieve:
template<typename T>
class Foo
{
public:
Foo(T t, const char* s) : first(t), second(s)
{
// do something
}
private:
T first;
const char* second;
};
This would work for any type, for the first parameter: int, float, double, whatever.
Now if you really want to restrict the type of the first parameter to be only int, float or double; you can come up with something more elaborate like
template<typename T>
struct RestrictType;
template<>
struct RestrictType<int>
{
typedef int Type;
};
template<>
struct RestrictType<float>
{
typedef float Type;
};
template<>
struct RestrictType<double>
{
typedef double Type;
};
template<typename T>
class Foo
{
typedef typename RestrictType<T>::Type FirstType;
public:
Foo(FirstType t, const char* s) : first(t), second(s)
{
// do something
}
private:
FirstType first;
const char* second;
};
int main()
{
Foo<int> f1(0, "can");
Foo<float> f2(1, "i");
Foo<double> f3(1, "have");
//Foo<char> f4(0, "a pony?");
}
If you remove the comment on the last line, you'll effectively get a compiler error.
String literals are not allowed by C++2003
ISO/IEC 14882-2003 §14.1:
14.1 Template parameters
A non-type template-parameter shall have one of the following (optionallycv-qualified) types:
— integral or enumeration type,
— pointer to object or pointer to function,
— reference to object or reference to function,
— pointer to member.
ISO/IEC 14882-2003 §14.3.2:
14.3.2 Template non-type arguments
A template-argument for a non-type, non-template template-parameter shall be one of:
— an integral constant-expression of integral or enumeration type; or
— the name of a non-type template-parameter; or
— the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as & id expression where the & is optional if the name refers to a function or array, or if the corresponding template-parameter is a reference; or
— a pointer to member expressed as described in 5.3.1.
[Note:A string literal (2.13.4) does not satisfy the requirements of any of these categories and thus is not an acceptable template-argument.
[Example:
template<class T, char* p> class X {
//...
X();
X(const char* q) { /* ... */ }
};
X<int,"Studebaker"> x1; //error: string literal as template-argument
char p[] = "Vivisectionist";
X<int,p> x2; //OK
—end example] —end note]
And it looks like it's not going to change in the upcoming C++0X, see the current draft 14.4.2 Template non-type arguments.
Based on your comments under Niel's answer, another possibility is the following:
#include <iostream>
static const char* eventNames[] = { "event_A", "event_B" };
enum EventId {
event_A = 0,
event_B
};
template <int EventId>
class Event
{
public:
Event() {
name_ = eventNames[EventId];
}
void print() {
std::cout << name_ << std::endl;
}
private:
const char* name_;
};
int main()
{
Event<event_A>().print();
Event<event_B>().print();
}
prints
event_A
event_B
You cannot pass a string literal directly as a template parameter.
But you can get close:
template<class MyString = typestring_is("Hello!")>
void MyPrint() {
puts( MyString::data() );
}
...
// or:
MyPrint<typestring_is("another text")>();
...
All you need is a small header file from here.
Alternatives:
Define a global char const * and pass it to the template as pointer. (here)
Drawback: Requires additional code outside of the template argument list. It is not suitable, if you need to specify the string literal "inline".
Use a non-standard language extension. (here)
Drawback: Not guaranteed to work with all compilers.
Use BOOST_METAPARSE_STRING. (here)
Drawback: Your code will depend on the Boost library.
Use a variadic template parameter pack of char, e.g. str_t<'T','e','s','t'>.
This is what the above solution does for you behind the scenes.
Use proxy static constexpr const char type_name_str[] = {"type name"}; for passing string as template parameter. Defining string using [] is important.
#include <iostream>
template<typename T, const char* const t_name>
struct TypeName
{
public:
static constexpr const char* Name()
{
return t_name;
};
};
static constexpr const char type_name_str[] = {"type name"};
int main()
{
std::cout<<TypeName<float, type_name_str>::Name();
return 0;
}
I want a class which takes two parameters in its constructor. The first can be either an int, double or float, so , and the second is always a string literal "my string"
template<typename T>
class demo
{
T data;
std::string s;
public:
demo(T d,std::string x="my string"):data(d),s(x) //Your constructor
{
}
};
I am not sure but is this something what you want?
Maybe not what the OP is asking, but if you use boost, you can create a macro like this for example:
#define C_STR(str_) boost::mpl::c_str< BOOST_METAPARSE_STRING(str_) >::value
Then use as follows:
template<const char* str>
structe testit{
};
testit<C_STR("hello")> ti;
template <char... elements>
struct KSym /* : optional_common_base */ {
// We really only care that we have a unique-type and thus can exploit being a `""_ksym singleton`
const char z[sizeof...(elements) + 1] = { elements..., '\0' };
// We can have properties, we don't need anything to be constexpr for Rs
};
template <typename T, T... chars>
auto&& operator""_ksym() {
static KSym<chars...> kSym; // Construct the unique singleton (lazily on demand)
return kSym;
}
static auto ksym_example1 = "a unique string symbol1\n"_ksym.z;
static auto ksym_example2 = "a unique string symbol2\n"_ksym.z;
auto dont_care = []() {
::OutputDebugString(ksym_example1);
::OutputDebugString("a unique string symbol2\n"_ksym.z);
assert("a unique string symbol1\n"_ksym.z == ksym_example1);
assert("a unique string symbol2\n"_ksym.z == ksym_example2);
return true;
}();
The above is working for me in production using Clang 11 on Windows.
(edited) I now use exactly this in clang on Windows:
// P0424R1: http://www.open-std.org/jtc1/SC22/wg21/docs/papers/2017/p0424r1.pdf
template <char... chars_ta> struct KSymT;
template <typename T, T... chars_ta> // std::move(KSymT<chars_ta...>::s);
auto operator""_ksym()->KSymT<chars_ta...>& { return KSymT<chars_ta...>::s; }
struct KSym {
virtual void onRegister() {}
virtual std::string_view zview_get() = 0;
};
template <char... chars_ta>
struct KSymT : KSym {
inline static KSymT s;
// We really only care that we have a unique-type and thus can exploit being a `""_ksym singleton`
inline static constexpr char z[sizeof...(chars_ta) + 1] = { chars_ta..., '\0' };
inline static constexpr UIntPk n = sizeof...(chars_ta);
// We can have properties, we don't need anything to be constexpr for Rs
virtual std::string_view zview_get() { return std::string_view(z); };
//#KSym-support compare with `Af_CmdArgs`
inline bool operator==(const Af_CmdArgs& cmd) {
return (cmd.argl[0] == n && memcmp(cmd.argv[0], z, n) == 0);
}
};
I was struggling with a similar problem and finally came up with a concise implementation that unpacks the string literal into a char... template parameter pack and without using the GNU literal operator template extension:
#include <utility>
template <char ...Chars>
struct type_string_t {
static constexpr const char data[sizeof...(Chars)] = {Chars...};
};
template <char s(std::size_t), std::size_t ...I>
auto type_string_impl(std::index_sequence<I...>) {
return type_string_t<s(I)...>();
}
#define type_string(s) \
decltype (type_string_impl<[] -> constexpr (std::size_t i) {return s[i];}> \
(std::make_index_sequence<sizeof (s)>()))
static_assert (std::is_same<type_string("String_A"),
type_string("String_A")>::value);
static_assert (!std::is_same<type_string("String_A"),
type_string("String_B")>::value);
A major caveat: this depends on a C++20 feature (class values as non-type template arguments; P0732, P1907), which (as of December 2020) is only (partially) implemented in GCC 9 and later (preprocessor feature test: (__cpp_nontype_template_args >= 201911L) || (__GNUG__ >= 9)). However, since the feature is standard, it is only a matter of time before other compilers catch up.
Another C++20 solution I don't see mentioned, but which was sufficiently simple and suitable for my own needs, is to use a constexpr lambda as the NTTP returning the string:
#include <string_view>
template<auto getStrLambda>
struct MyType {
static constexpr std::string_view myString{getStrLambda()};
};
int main() {
using TypeWithString = MyType<[]{return "Hello world!";}>;
return 0;
}
Compiler explorer example here.
here is a solution and extensions/examples
my solution extends https://ctrpeach.io/posts/cpp20-string-literal-template-parameters/
#include <iostream>
#include <algorithm>
#include <string>
template<size_t N>
struct StringLiteral {
char value[N];
constexpr StringLiteral(const char(&str)[N]) {
std::copy_n(str, N, value);
}
};
template <StringLiteral T>
struct String {
static constexpr std::string str() {
return T.value;
}
};
template <typename... Strings>
struct JoinedString {
static constexpr std::string str() {
return (Strings::str() + ...);
}
};
template <typename Delim, typename String, typename... Strings>
struct DelimJoinedString {
static constexpr std::string str() {
if constexpr (sizeof...(Strings))
return JoinedString<String, Delim, DelimJoinedString<Delim, Strings...>>::str();
else
return String::str();
}
};
int main() {
// "123"
using s123 = String<"123">;
std::cout << s123::str() << "\n";
// "abc"
using abc = String<"abc">;
std::cout << abc::str() << "\n";
// "abc123abc123"
using abc123abc123 = JoinedString<abc, s123, abc, s123>;
std::cout << abc123abc123::str() << "\n";
// "abc, 123"
using abccomma123 = DelimJoinedString<String<", ">, abc, s123>;
std::cout << abccomma123::str() << "\n";
// "abc, 123, 123, abc"
using commaabc123123abc = DelimJoinedString<String<", ">, abc, s123, s123, abc>;
std::cout << commaabc123123abc::str() << "\n";
return 0;
}
a string literal "my string", so I guess const char * const
Actually, string literals with n visible characters are of type const char[n+1].
#include <iostream>
#include <typeinfo>
template<class T>
void test(const T& t)
{
std::cout << typeid(t).name() << std::endl;
}
int main()
{
test("hello world"); // prints A12_c on my compiler
}