Passing a string literal as a type argument to a class template
Asked Answered
B

17

71

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.

Bohaty answered 9/1, 2010 at 10:57 Comment(2)
If you mean template parameters, see Neil's answer below; but if you mean ctor parameters (as it seems you wrote?) then you don't even need a template. Please clarify.Equivalence
The string literal "my string" is of type const char[10]Costumer
R
26

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" );
Riana answered 9/1, 2010 at 10:58 Comment(11)
Do C++0x variadic templates support integral arguments? You could probably do something nasty with representing strings as lists of chars.Wince
maybe you might hack around with 4 char long literals (supported by all compilers?) and variadic templates -- it would make a nice answer on SO but it would look ugly in production code :)Rockie
Neil, that is looking very good, but since I am dumb, can you correct this code? I tried to adapt yours to take two params, which ought to be simple enough ... <pre> template<typename E, typename S> class Event { public: Event(E eventId, S eventName); // constructor private: E _eventId; char _eventName[MAX_EVENT_NAME_LENGTH + 1]; }; </pre> and try to instantiate with <pre> enum enum1 {eventA, eventB}; Event<enum1, const char *> testEventA(eventA, "A"); </pre> but I get compiler errors - see next comment, running out of spaceBohaty
sht!! How to format comments, if PRE doesn't work? - trying to instantiate ‘template<class E, class S> class - initializer expression list treated as compound expression - invalid conversion from ‘const char’ to ‘int’ test_fsm.cpp - invalid type in declaration before ‘(’ token test_fsm.cpp - template argument for ‘template<class E, class S> class Event’ uses local type ‘testFsmClasses::TesEventConstructor()::enum1’Bohaty
@Bohaty Edit your original question. ANd never try to use HTML to format the question or your answers. Use those little buttons above the editor.Riana
Hi, Neil, Ideally I would have preferred an example with two params, one being an enum value and the other a string, being the event name. By hacking your code, I go it to work - but needed to use a #define as I can't make it work otherwise - because you can't pass a string literal as a template parameter. Here's what I cam up with: (next comment, not enough space here)Bohaty
template <typename EventId, typename EventName> class Event { public: Event(EventId ventId, EventName eventName ) {} }; enum event{eventA, eventB}; #define DECLARE_EVENT(w, x, y, z) Event<x, const char *> w(y, z ) int main(int argc, char *argv[]) { DECLARE_EVENT(e1, event, eventA,"event_A"); DECLARE_EVENT(e2, event, eventA,"event_B"); return 0; }Bohaty
hmm, I though that four spaces at the start of each line would make it a pre-formatted code block (????)Bohaty
Neil, sorry to be so dumb - but the buttons - are they there for comments? I do see them for answers. and, from what I read - from the buttons - four spaces ought to PRE CODE - sorry to be so dumb...Bohaty
@pete - I just did exactly that to see if it could be done: #1826964Hundredpercenter
Important Note: You can have a const char* non-type template parameter, which is not all that far from passing a string literal. coliru.stacked-crooked.com/a/64cd254136dd0272Costumer
C
65

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

Costumer answered 5/12, 2017 at 19:37 Comment(5)
This should be pinned answer. I wish I didn't miss this before I find out it by myself... a month later than I need this.Byre
Also a global variable has a well defined identity; string literals, not so much (are "hello" and "hello" the same object)?Acree
@curiousguy: "Sometimes", but you're right that that's a risk/quirk hereCostumer
looks ok as well with constexpr char teststr[] = "Hello world!";Ullund
This does not seem to work pre-C++11 (still deserves a +1 though!): godbolt.org/z/YWchsr465Wanton
C
41

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
Concord answered 9/1, 2010 at 12:23 Comment(4)
This looks interesting. Can it be massages to pass the string as a parameter and show and example of declaring an object?Bohaty
@mawg As per your example under Niel's answer (really you should update your question, saying "Update: This is what I want"), you want to differentiate between classes based (only) on a string template parameter. This is impossible (see Niel's answer). But if you want to differentiate between classes based on EventId, then you can use the EventName as a field in the trait class as per my answer.Concord
@mawg Also see my another newly added answer.Concord
This is just what I needed. I tuned it a bit with one preprocessor macro which takes three parameters: A type name, a string literal, and the class template parameter(s). The macro then defines the template specialization for the template parameter(s), sets the trait to the string literal, and finally typedefs the class template with the template parameters as the type name. So using this mechanism is reduced to one simple preprocessor macro call instead of a dozen lines of C++ template code.Chrysotile
F
35

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?

Fidel answered 13/11, 2019 at 16:47 Comment(6)
As per Aug'21 the following compilers support: msvc v19.29 clang 12.0 gcc 9.1 icx 2021.2.0Pinole
In the code above, what does this line define? template<unsigned N> FixedString(char const (&)[N]) -> FixedString<N - 1>;Pinole
@Pinole ah, great news about the support! I have to check! I don't know what the line means to be honest, I just copied pasted but didn't try to understand the insane new syntax. Let me know if you manage to figure it out.Fidel
Your example that failed to compile was due to hello not being defined. It compiled for me on g++ 9.4.0 for Ubuntu after giving it a definition.Punishable
@Pinole It's a user-defined deduction guidePunishable
To add to what @Punishable said, if you encountered ld: symbol(s) not found for architecture x86_64, consider giving void hello() a definition, e.g. changing void hello() const; to void hello() const { std::cout << Name << std::endl; } and also adding #include <iostream> at the beginning. This should compile at least with gcc 12 or clang 14 on macOS, for example g++ -std=c++20 hello.cpp. You should see Hello! printed out by running the program.Whomp
R
26

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" );
Riana answered 9/1, 2010 at 10:58 Comment(11)
Do C++0x variadic templates support integral arguments? You could probably do something nasty with representing strings as lists of chars.Wince
maybe you might hack around with 4 char long literals (supported by all compilers?) and variadic templates -- it would make a nice answer on SO but it would look ugly in production code :)Rockie
Neil, that is looking very good, but since I am dumb, can you correct this code? I tried to adapt yours to take two params, which ought to be simple enough ... <pre> template<typename E, typename S> class Event { public: Event(E eventId, S eventName); // constructor private: E _eventId; char _eventName[MAX_EVENT_NAME_LENGTH + 1]; }; </pre> and try to instantiate with <pre> enum enum1 {eventA, eventB}; Event<enum1, const char *> testEventA(eventA, "A"); </pre> but I get compiler errors - see next comment, running out of spaceBohaty
sht!! How to format comments, if PRE doesn't work? - trying to instantiate ‘template<class E, class S> class - initializer expression list treated as compound expression - invalid conversion from ‘const char’ to ‘int’ test_fsm.cpp - invalid type in declaration before ‘(’ token test_fsm.cpp - template argument for ‘template<class E, class S> class Event’ uses local type ‘testFsmClasses::TesEventConstructor()::enum1’Bohaty
@Bohaty Edit your original question. ANd never try to use HTML to format the question or your answers. Use those little buttons above the editor.Riana
Hi, Neil, Ideally I would have preferred an example with two params, one being an enum value and the other a string, being the event name. By hacking your code, I go it to work - but needed to use a #define as I can't make it work otherwise - because you can't pass a string literal as a template parameter. Here's what I cam up with: (next comment, not enough space here)Bohaty
template <typename EventId, typename EventName> class Event { public: Event(EventId ventId, EventName eventName ) {} }; enum event{eventA, eventB}; #define DECLARE_EVENT(w, x, y, z) Event<x, const char *> w(y, z ) int main(int argc, char *argv[]) { DECLARE_EVENT(e1, event, eventA,"event_A"); DECLARE_EVENT(e2, event, eventA,"event_B"); return 0; }Bohaty
hmm, I though that four spaces at the start of each line would make it a pre-formatted code block (????)Bohaty
Neil, sorry to be so dumb - but the buttons - are they there for comments? I do see them for answers. and, from what I read - from the buttons - four spaces ought to PRE CODE - sorry to be so dumb...Bohaty
@pete - I just did exactly that to see if it could be done: #1826964Hundredpercenter
Important Note: You can have a const char* non-type template parameter, which is not all that far from passing a string literal. coliru.stacked-crooked.com/a/64cd254136dd0272Costumer
R
11

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

Regan answered 9/8, 2013 at 19:55 Comment(0)
W
10
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.

Wastebasket answered 31/3, 2011 at 12:33 Comment(1)
Answer by Mooing Duckby to this question is even better – https://mcmap.net/q/213218/-passing-a-string-literal-as-a-type-argument-to-a-class-templateSpasmodic
R
6

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.

Rockie answered 9/1, 2010 at 11:15 Comment(2)
Gregory, I have read similar explanation elsewhere, but not so clear. Your code looks like I can understand it, and it should work - but it gives error under Linux with G++ (in fact, I have compile errors with two good looking suggestions above - can I really be so dumb?)Bohaty
I copied your code and it still doesn't work. Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for ONLINE_EVALUATION_BETA2 Copyright 1988-2008 Comeau Computing. All rights reserved. MODE:strict errors C++ C++0x_extensions "ComeauTest.c", line 14: error: expression must have a constant value X<int,p> x2; //OKExhalation
C
5

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
Concord answered 13/1, 2010 at 9:1 Comment(4)
Yes, that works, and is worth considering. What worries me, though, is that it is possible to make a mistake and get them out of alignment. It would be 'better' to pass both the enum value and the string as a pair (but, of course, you can still make mistakes there too)Bohaty
I'd use the enum values with the # operator to generate the string representations.Wealth
This is the cleanest answer, but if used in header files it will cause problems during linking , i.e. duplicate definitions, etc. The accepted answer uses templates on a struct to help the linker marry up the definitions at compile time.Bangka
Keep in mind this won't work if two enum entries share the same underlying value. They will overlap in the eventNames[] lookup.Tressa
V
5

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.

Varicelloid answered 27/7, 2017 at 9:29 Comment(1)
typestring_is here is a macro similar to BOOST_METAPARSE_STRINGIrbm
E
5

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;
}
Eileneeilis answered 10/3, 2020 at 7:32 Comment(0)
M
2

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?

Millenarianism answered 9/1, 2010 at 11:3 Comment(4)
Might want to make some part of that class public, or just use struct in examples, which greatly reduces clutter. :)Equivalence
That is looking very close! But how can I pass a different "my string" as constructor parameter each time I instantiate?Bohaty
@mawg:You need not pass "my string". Just try this: demo<int> d(1); // second parameter is "my string" by default demo<double> d1(1.4); // second parameter is again "my string" by defaultMillenarianism
@mawg: Read about default argument constructors here: people.cs.vt.edu/~kafura/cs2704/default.htmlMillenarianism
C
2

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.

Comintern answered 10/8, 2018 at 19:7 Comment(4)
what compiler did you use ? fails to compile with MSVC c++17Baylor
Agreed. What compiler are you using. Doesn't work on any c++17 compiler that I've seen. This is because you can't declare a lambda in a template parameter.Aerialist
It’s C++20, and it currently only works in GCC 9 and later. I know because I have rediscovered something similar recently.Ichthyoid
Very nice, but could not get it to compile on VS. To get it to work I changed the macro to: #define type_string(s) decltype(type_string_impl<[](size_t i) {return s[i];}> (make_index_sequence<sizeof(s)-1>{})). Note the removal of the ->constexpr part.Enrollee
B
2

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;
Baylor answered 17/1, 2019 at 10:20 Comment(1)
How this C_STR will fully expand? Can you give an example?Shoe
P
2
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);
  }
};
Peale answered 19/5, 2020 at 1:49 Comment(0)
T
2

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;
}
Thymelaeaceous answered 31/1, 2023 at 7:7 Comment(0)
S
1

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.

Stilton answered 13/10, 2022 at 16:20 Comment(0)
I
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
}
Isabelleisac answered 9/1, 2010 at 12:46 Comment(1)
This passes the type of the string literal as template parameter, not the string literal itself.Varicelloid

© 2022 - 2024 — McMap. All rights reserved.