52

Is it possible in "modern C++" (C++17 or greater) to pass a string literal as a parameter to a C++ template?

I realize you could do this with constructor argument; I just thought it would be more convenient to have it as a template argument, rather than buried deep in the cpp file. I was curious if maybe this was a new feature of modern C++. See Pseudo code below of what I'm trying to do:

Pseudo-code Example:

// Header File /////////////////////////
template<constexpr string Name>
class ModuleBase {
public:
    ModuleBase();
    string name;
};

class xyz : ModuleBase<"xyz"> {
public:
    xyz();
};

// Cpp File //////////////////////////
template<string_literal Name>
ModuleBase<Name>::ModuleBase() {
    name = Name;
}

xyz::xyz() : ModuleBase() {

}
AMA
  • 3,948
  • 15
  • 31
Bimo
  • 5,191
  • 1
  • 33
  • 49
  • 2
    FWIW, You cannot put a template class's definitions in a cpp file: https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file – NathanOliver Jul 05 '18 at 15:48
  • 4
    @NathanOliver you can, with lengthy caveats – Caleth Jul 05 '18 at 15:50
  • @Nathan That answer doesn't exactly say that - you certainly _can_ put a template definition in a C++ source file, though it mostly doesn;'t do what you want. –  Jul 05 '18 at 15:50
  • 2
    Yeah, I know, but it is a lot easier to just let the OP read the actual explanation then try and summarize it all in a comment.. – NathanOliver Jul 05 '18 at 15:51
  • You can do it... it just has a weird syntax... template MyClass::Method(T x) { ... }... However, I haven't had any luck getting constructors to accept this syntax using visual studio 2015... but a general method it works fine... – Bimo Jul 05 '18 at 15:51
  • I see where somebody proposed it recently: www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0424r2.pdf – Bimo Jul 05 '18 at 15:53
  • 5
    @BillMoore The problem isn't the syntax, the problem is the semantics - the template _definition_ (not just the declaration!) needs to be visible at usage side - so you'd basically have to either use it like a header anyway (or else only use it locally). – Cubic Jul 05 '18 at 15:54
  • 1
    https://stackoverflow.com/questions/1826464/c-style-strings-as-template-arguments – n. 1.8e9-where's-my-share m. Jul 05 '18 at 15:59
  • @BillMoore, According to [this trip report](https://botondballo.wordpress.com/2018/03/28/trip-report-c-standards-meeting-in-jacksonville-march-2018/), that paper was accepted by EWG previously, but ultimately pulled, leaving the change in the the below answer the only solution. – chris Jul 05 '18 at 16:11
  • 1
    Am I guessing right that you don't like doing it the old way, declaring the template as `ModuleBase`, having an `extern char const *xyz="xyz"`and then specializing with `ModuleBase` ? – Spencer Jul 05 '18 at 17:24
  • I am not sure if you can do that, but maybe you can get away with first making a compile-time hash of the string literal and then using that hash as a template parameter. You can use this little piece of code I wrote for compile-time hashing of string literals: https://github.com/WojciechMigda/static-string-hash Then you can write: `ModuleBase` – Wojciech Migda Jul 07 '18 at 22:48
  • Ive seen that done in c++11 using char[]. Can’t remember well. That’s why it’s just a comment. – Regis Portalez Jul 09 '18 at 15:56

2 Answers2

57

Yes, in .

The problem was that determining uniqueness of a template non-type argument was difficult.

adds in a <=> spaceship operator comparison. If it is non-user provided (and based only off non-user provided <=> in turn, repeat recursively) (and a few other requirements; see p0732), the type can be used as a non-type template argument.

Such types can be constructed from raw "strings" in constexpr constructors, including using deduction guides to make them auto-size themselves.

As the size of the data stored is probably going to be part of the type, you'll want to take the type as an auto typed non-type parameter or otherwise auto-deduced type.


Note that placing the implementation of your template in a cpp file is usually a bad idea. But that is another question.

Acorn
  • 23,483
  • 4
  • 35
  • 66
Yakk - Adam Nevraumont
  • 250,370
  • 26
  • 305
  • 497
  • 1
    This was accepted in EWG, but AFAIK, it still has to go through CWG before it's actually in C++20 (with usual caveats of C++20 not being done until it's done). It really is looking likely, however. – chris Jul 05 '18 at 16:09
  • 18
    I am unreasonably happy that `<=>` is widely known as "the spaceship operator". – Ti Strga Jul 05 '18 at 19:25
  • 1
    @TiStrga It was named that even in the introductory paper: http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0515r0.pdf pew pew – Yakk - Adam Nevraumont Jul 05 '18 at 19:40
  • 2
    @chris You're out of date, it was approved by CWG and plenary voted it in :) – Rakete1111 Jul 05 '18 at 22:37
  • @Rakete1111, My mistake. I skimmed over that section too quickly when looking for confirmation. Semi-interesting that this is listed twice in that trip report. – chris Jul 06 '18 at 07:01
  • Actually, the spaceship operator doesn't have to be `constexpr` to use the class as non-type parameter :) – Rakete1111 Jul 06 '18 at 08:55
  • 1
    @Rakete1111 No? "have a non-user-provided operator<=> returning a type that is implicitly convertible to std::strong_equality , and contain no references." -- the requirement is stronger than `constexpr`, it is non-user provided recursively (which is `constexpr`) – Yakk - Adam Nevraumont Jul 06 '18 at 13:14
  • @Yakk Yes it's stronger but it doesn't mean that its implicitly or is required to be constexpr AFAIK. – Rakete1111 Jul 06 '18 at 17:35
1

Until you get and if you have , you may find the following macro usefull:

#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;
darune
  • 9,964
  • 2
  • 19
  • 53