Unexpected Warning: inline function used but never defined - c++

I searched online for the similar warnings; most people run into the similar warning because they defined the inline function in another translation unit (e.g. the cpp file).
But I put the definition within the same unit.
#include <iostream>
#include <string.h>
template <typename T>
struct MagicInt { constexpr static const char* str(); };
template <>
struct MagicInt<int8_t> {
constexpr static const char* str() {
return "123";
}
};
template<typename T>
inline char* foo(char *buf, T t) {
if (t < T(5)) return (char*)memcpy(buf, MagicInt<T>::str(), strlen(MagicInt<T>::str())) + strlen(MagicInt<T>::str());
return buf;
}
int main() {
char buffer[1024];
buffer[0] = '1';
buffer[1] = '0';
std::cout << foo(buffer, uint16_t(123)) << std::endl;
}
$ g++ -Wall iwarn.cpp -o iwarn -lrt -std=c++0x -O3
iwarn.cpp:5:48: warning: inline function ‘static constexpr const char* MagicInt<T>::str() [with T = short unsigned int]’ used but never defined [enabled by default]
struct MagicInt { constexpr static const char* str(); };
^
My gcc version:
$ g++ --version
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11)
It looks to me that gcc was looking for the definition of MagicInt<uint16_t>::str().
I can think of 3 ways to fix it:
1. defining MagicInt<uint16_t>::str(), tho it will never be called
2. providing a default implementation (in addition to the partial template specialization)
3. suppress the warning?
Which one is cleaner? Which one generates less code (probably #4 -- ignoring the warning)?

Yes, the compiler is looking for the definition of MagicInt<uint16_t>::str().
The reason it is doing so is
The call of foo(buffer, uint16_t(123)) (with result streamed to std::cout is an exact match for the templated foo<uint16_t>(). Since you have explicitly forced the second argument of foo() to be of type uint16_t that should not be surprising.
To instantiate foo<uint16_t>() (for which the template parameter T is uint16_t) the compiler must instantiate MagicInt<uint16_t>. If it does not, it cannot compile the statement
return (char*)memcpy(buf, MagicInt<T>::str(), strlen(MagicInt<T>::str())) + strlen(MagicInt<T>::str());
The fact that main() has passed uint16_t(123) is irrelevant. The instantiation of the template above is based on the type (T) of the function parameter, not the value passed. The exact same logic as above would be used if main() instead did;
int main()
{
char buffer[1024];
buffer[0] = '1';
buffer[1] = '0';
uint16_t v;
std::cin >> v;
std::cout << foo(buffer, v) << std::endl;
}
because it is the type of v that matters in instantiating the template, not the value passed (regardless of whether that value is hard-coded - expressed as a literal - or determined at runtime (like I have done here)).
The simple ways to fix the problem are to define the functions being instantiated. Either of your options 1 and 2 will do that job.
Option 3 (assuming it is possible - I haven't checked) will suppress the warning but - in a typical "compile-link" toolchain, the typical result will be a linker error - because MagicInt<uint16_t>::str() will still not be defined.
The real solution is to adjust your expectations. The definition
template<typename T>
inline char* foo(char *buf, T t) {
if (t < T(5)) return (char*)memcpy(buf, MagicInt<T>::str(), strlen(MagicInt<T>::str())) + strlen(MagicInt<T>::str());
return buf;
}
is required to treated as if the value of t is unknown at compile time. In other words, the compiler is not required to go through a sequence of detecting that the value passed is uint16_t<123), determining that t < T(5) is always false, and then not instantiating MagicInt<uint16_t>.

The problem is that you have specialized the template for int8_t but you are using uint16_t. The uint16_t version of MagicInt was never defined.

If your argument is const expression, you might provide it in template parameter:
template<typename T, T t>
std::enable_if_t<(t < T(5)), char*>
char* foo(char *buf) {
return (char*)memcpy(buf, MagicInt<T>::str(), strlen(MagicInt<T>::str()))
+ strlen(MagicInt<T>::str());
}
template<typename T, T t>
std::enable_if_t<!(t < T(5)), char*>
char* foo(char *buf) {
return buf;
}
or in C++17
template<typename T, T t>
char*
char* foo(char *buf) {
if constexpr (t < T(5)) {
return (char*)memcpy(buf, MagicInt<T>::str(), strlen(MagicInt<T>::str()))
+ strlen(MagicInt<T>::str());
} else {
return buf;
}
}
Then MagicInt<std::uint16_t>::str() wont be instantiated.

Related

C++ Get Name of Function from Template

for Debugging purposes I'd like to extract the name of the function from a template argument. However I'm only getting the functions signature not an actual name.
namespace internal
{
static const unsigned int FRONT_SIZE = sizeof("internal::GetTypeNameHelper<") - 1u;
static const unsigned int BACK_SIZE = sizeof(">::GetTypeName") - 1u;
template<typename T>
struct GetTypeNameHelper
{
static const char* GetTypeName(void)
{
#ifdef __GNUC__
static const size_t size = sizeof(__PRETTY_FUNCTION__);
static char typeName[size] = { };
memcpy(typeName, __PRETTY_FUNCTION__, size - 1u);
#else
static const size_t size = sizeof(__FUNCTION__) - FRONT_SIZE - BACK_SIZE;
static char typeName[size] =
{};
memcpy(typeName, __FUNCTION__ + FRONT_SIZE, size - 1u);
#endif //__GNUC__
return typeName;
}
};
} //namespace internal
template<typename T>
const char* GetTypeName(void)
{
return internal::GetTypeNameHelper<T>::GetTypeName();
}
Calling this from an own make function
template<typename Func_T, typename ... Args>
CExtended_Function<Args...> Make_Extended_Function(Func_T f)
{
std::function<void(Args...)> func(f);
const char* pFunc_Name = NCommonFunctions::GetTypeName<Func_T>();
CExtended_Function<Args...> res(f, func_name);
return res;
}
with
void Test_Func();
void foo()
{
Make_Extended_Function(Test_Func);
}
Gives me only the function signature.
... [with T = void (*)()]...
However I'd like to get the function name (in this case "Test_Func")
I thought about using makros but I'm not sure how to implement the Args... Part in Makros. Do you have an idea on how to solve this? I'd like to avoid using RTTI.
Functions aren't valid template arguments - your template argument here is the type of a pointer to the function, not the function itself - so this is completely impossible. There is also no portable way to get the name of a particular function at compile time either, at least at the moment (it's possible that this will be possible in the future through compile time reflection, but that's going to be C++2y (23?) at the earliest).
With Macro, you can do (I also use CTAD from C++17)
template<typename F>
auto Make_Extended_Function_Impl(F f, const std::string& name)
{
std::function func(f);
CExtended_Function res(f, name);
return res;
}
#define Make_Extended_Function(f) Make_Extended_Function(f, #f)

c++: using constexpr to XOR data doesn't work

Here is my code:
template<int... I>
class MetaString1
{
public:
constexpr MetaString1(constexpr char* str)
: buffer_{ encrypt(str[I])... } { }
const char* decrypt()
{
for (int i = 0; i < sizeof...(I); ++i)
buffer_[i] = decrypt1(buffer_[i]);
buffer_[sizeof...(I)] = 0;
return buffer_;
}
private:
constexpr char encrypt(constexpr char c) const { return c ^ 0x55; }
constexpr char decrypt1(constexpr char c) const { return encrypt(c); }
private:
char buffer_[sizeof...(I)+1];
};
#define OBFUSCATED1(str) (MetaString1<0, 1, 2, 3, 4, 5>(str).decrypt())
int main()
{
constexpr char *var = OBFUSCATED1("Post Malone");
std::cout << var << std::endl;
return 1;
}
This is the code from the paper that I'm reading Here. The Idea is simple, to XOR the argument of OBFUSCATED1 and then decrypt back to original value.
The problem that I'm having is that VS 2017 gives me error saying function call must have a constant value in constant expression.
If I only leave OBFUSCATED1("Post Malone");, I have no errors and program is run, but I've noticed that if I have breakpoints in constexpr MetaString1 constructor, the breakpoint is hit, which means that constexpr is not evaluated during compile time. As I understand it's because I don't "force" compiler to evaluate it during compilation by assigning the result to a constexpr variable.
So I have two questions:
Why do I have error function call must have a constant value in constant expression?
Why do people use template classes when they use constexpr functions? As I know template classes get evaluated during compilation, so using template class with constexpr is just a way to push compiler to evaluate those functions during compilation?
You try to assign a non constexpr type to a constexpr type variable,
what's not possible
constexpr char *var = OBFUSCATED1("Post Malone")
// ^^^ ^^^^^^^^^^^
// type of var is constexpr, return type of OBFUSCATED1 is const char*
The constexpr keyword was introduced in C++11, so before you had this keyword you had to write complicated TMP stuff to make the compiler do stuff at compile time. Since TMP is turing complete you theoretically don't need something more than TMP, but since TMP is slow to compile and ugly to ready, you are able to use constexpr to express things you want evaluate at compile time in a more readable way. Although there is no correlation between TMP and constexpr, what means, you are free to use constexpr without template classes.
To achieve what you want, you could save both versions of the string:
template <class T>
constexpr T encrypt(T l, T r)
{
return l ^ r;
}
template <std::size_t S, class U>
struct in;
template <std::size_t S, std::size_t... I>
struct in<S, std::index_sequence<I...>>
{
constexpr in(const char str[S])
: str_{str[I]...}
, enc_{encrypt(str[I], char{0x12})...}
{}
constexpr const char* dec() const
{
return str_;
}
constexpr const char* enc() const
{
return enc_;
}
protected:
char str_[S];
char enc_[S];
};
template <std::size_t S>
class MetaString1
: public in<S, std::make_index_sequence<S - 1>>
{
public:
using base1_t = in<S, std::make_index_sequence<S - 1>>;
using base1_t::base1_t;
constexpr MetaString1(const char str[S])
: base1_t{str}
{}
};
And use it like this:
int main()
{
constexpr char str[] = "asdffasegeasf";
constexpr MetaString1<sizeof(str)> enc{str};
std::cout << enc.dec() << std::endl;
std::cout << enc.enc() << std::endl;
}

Had to specify upper limit while evaluating the length of C-style string through template metaprogramming

I recently starting playing around with template metaprogramming in C++, and been trying to evaluate the length of a C-style string.
I've had some success with this bit of code
template <const char *str, std::size_t index>
class str_length {
public:
static inline std::size_t val() {
return (str[index] != '\0') ? (1 + str_length<str, index + 1>::val()) : 0;
}
};
template <const char *str>
class str_length <str, 500> {
public:
static inline std::size_t val() {
return 0;
}
};
extern const char bitarr[] { "0000000000000000000" };
int main() {
std::cout << str_length<bitarr, 0>::val() << std::endl;
getchar();
return 0;
}
However, I had to set a "upper limit" of 500 by creating a specialization of str_length. Omitting that would cause my compiler to run indefinitely (presumably creating infinite specializations of str_length).
Is there anything I could do to not specify the index = 500 limit?
I'm using VC++2015 if that helps.
Oh, and I'm not using constexpr because VC++ doesn't quite support the C++14 extended constexpr features yet. (https://msdn.microsoft.com/en-us/library/hh567368.aspx#cpp14table)
The usual way to stop infinite template instantiation, in these situation, is by using specialization; which is orthogonal to constexpr-ness of anything. Reviewing the list of additional stuff that extended constexpr allows in C++14, I see nothing in the following example that needs extended constexpr support. gcc 6.1.1 compiles this in -std=c++11 compliance mode, FWIW:
#include <iostream>
template<const char *str, size_t index, char c> class str_length_helper;
template <const char *str>
class str_length {
public:
static constexpr std::size_t val()
{
return str_length_helper<str, 0, str[0]>::val();
}
};
template<const char *str, std::size_t index, char c>
class str_length_helper {
public:
static constexpr std::size_t val()
{
return 1+str_length_helper<str, index+1, str[index+1]>::val();
}
};
template<const char *str, std::size_t index>
class str_length_helper<str, index, 0> {
public:
static constexpr std::size_t val()
{
return 0;
}
};
static constexpr char bitarr[] { "0000000000000000000" };
int main() {
std::cout << str_length<bitarr>::val() << std::endl;
getchar();
return 0;
}
Note, however, that the character string itself must be constexpr. As the comments noted, this is of dubious practical use; but there's nothing wrong with messing around in this manner in order to get the hang of metaprogramming.
The key point is the use of specialization, and the fact that in order to be able to use str[index] as a template parameter, str must be constexpr.

Type-dependent constant in template function

I want a static array in a templated function whose length depends on the type with which the function is specialized. My first attempt was:
Header:
template<typename T>
struct Length {
const static size_t len;
};
template<typename T>
void func(){
static T vars[Length<T>::len]; // len not const. according to compiler!
// ...
}
Source file:
template<> const size_t Length<double>::len = 2;
template<> const size_t Length<float>::len = 1;
// ...
However, g++ does not compile this and complains
error: storage size of ‘vars’ isn’t constant
So, what exactly is the problem here? I know that the size of a fixed-length array needs to be a constant and known on compile time, but that seems to be the case here.
When I write
const size_t len = 2;
void func(){
static double vars[len];
}
it compiles without problems.
Question:
What is wrong with the code and which alternatives are there for achieving the desired behavior? I would not like to allocate the memory during runtime...
For a const variable to be considered a compile-time constant (formally, a constant expression), its value must be available at point of use. Which means the specialised definitions would have to go to the header file.
If done as just specialisations of the member, as you did, I believe that would give you a multiple-definition error. You should be fine with specialising the entire class template, and keeping the static member definition inline:
template<typename T>
struct Length;
template <>
struct Length<double>
{
static const size_t len = 2;
};
As a side note, your program was originally invalid: an explicit specialisation must be declared before use. Which means you'd have to at least declare the specialisation of len in the header (or everywhere you intended to use it).
The following code compiles fine for me with g++ 4.6.3 and produces the output
2
1
array.cpp:
#include <cstddef>
#include <iostream>
template<typename T>
struct Length {
const static size_t len;
};
template<typename T>
void func(){
static T vars[Length<T>::len];
std::cout << (sizeof(vars) / sizeof(*vars)) << std::endl;
}
template<> const size_t Length<double>::len = 2;
template<> const size_t Length<float>::len = 1;
int main(){
func<double>();
func<float>();
}
$ make array
g++ array.cpp -o array
$ ./array
2
1

Passing a string literal as a type argument to a class template

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
}