C++ Template const char array to int - c++

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;
}

Related

is a string converted to const char* in C++

A string like "hello" would be string or const char*.
Consider an example:
template<typename A>
A s(A a){
// ...
}
here what would be "hello" converted to, if I called s("hello")?
A string like "hello" is a const char[6]. Since you can't pass an array by value, A will be deduced to const char* instead.
When you are looking for a type you may use this trick :
Create a struct without implementation
template<typename A>
struct Error;
And use it :
template<typename A>
A s(A a){
Error<A> error;
return a;
}
int main()
{
Error<decltype("error")> e; // error: implicit instantiation of undefined template 'Error<char const (&)[6]>'
s("hello"); // error: implicit instantiation of undefined template 'Error<const char *>'
}
The error will give you the type you are looking for.
Tada! "Hello" type is char const [6] but in the s the decuce type is const char *
Credit :
Effective Modern C++, Chapter 1. Deducing Types, Item 4: Know how to view deduced types.
https://www.oreilly.com/library/view/effective-modern-c/9781491908419/ch01.html

Why member variables of a const object are not const

Just asked a similar question which boils down to this one.
#include <iostream>
using namespace std;
struct A {
A() : a{1} {};
int a;
};
template <typename Which>
struct WhichType;
int main() {
const A a;
const A& a_ref = a;
const A* a_ptr = &a;
WhichType<decltype(a.a)> which_obj; // template evaluates to int
WhichType<decltype(a_ref.a)> which_ref; // template evaluates to int
WhichType<decltype(a_ptr->a)> which_ptr; // template evaluates to int
return 0;
}
Why do the templates do not become const int instead of int?
decltype gives you the "declared type" of the operand when it isn't enclosed in an extra set of parentheses.
To get the actual type of the expression, that is, const int, you would have to write decltype((a.a)) and so on.
decltype always returns a reference type for lvalue expressions other than names.
When passed the name of an identifier (or member), it returns the type of declaration.
When passed a different expression, it returns something closer to what you want, but reference-qualified.
WhichType<std::remove_reference_t<decltype((a_ptr->a))>> which_ptr; // template evaluates to const int!
live example
or if you want the l/r valueness:
WhichType<decltype((a_ptr->a))> which_ptr2; // template evaluates to const int&
WhichType<decltype(((const A){}.a))> which_ptr3; // template evaluates to const int
you can append && to make it a "real" rvalue reference here.
WhichType<decltype(((A){}.a))&&> which_ptr4; // template evaluates to int&&!
live example.

how to decay array type to const pointer type in C++?

I would like to automatically generate const accessor function for given member but I struggle with arrays. It is possible to "decay" array type to a pointer, but I do not know how to make type of pointed value const? Any obvious method of adding const will only apply the pointer. Of course, I can make specialised accessor for array types, but it is not ideal solution. Returning const pointer to const value would also be acceptable. This is example of incomplete accessor:
auto foo() const -> const typename std::decay<decltype(foo_)>::type { return foo_; }
If you intend to get the address of a member array, simply qualify it as const
#include <iostream>
using namespace std;
struct fooType {
};
class MyClass {
public:
fooType foo_[2];
auto foo() const -> typename std::decay<const decltype(foo_)>::type
{ return &foo_[0]; }
};
int main() {
MyClass classObj;
classObj.foo();
return 0;
}
http://ideone.com/PjclAf
Edit:
The documentation states that
Applies lvalue-to-rvalue, array-to-pointer, and function-to-pointer
implicit conversions to the type T, removes cv-qualifiers, and defines
the resulting type as the member typedef type. This is the type
conversion applied to all function arguments when passed by value.
(emphasis mine)
The important takeaway here is that std::decay() always act to "simulate" a pass-by-value mechanism with the type you're feeding it. Cv-qualifiers are dropped iff they can be dropped in a pass-by-value call, not if they actually define the resulting type.
Take the following example:
#include <iostream>
#include <type_traits>
template <typename T, typename U>
struct decay_equiv :
std::is_same<typename std::decay<T>::type, U>::type
{};
void function1(int happyX) {
// happyX can be modified, it's just a local variable
happyX = 42;
std::cout << happyX << std::endl;
}
void function2(const int *ptrByValue) {
// ptrByValue can be modified, however its type is 'const int' and that CANNOT be modified
ptrByValue = (const int*)0xDEADBEEF;
std::cout << ptrByValue << std::endl;
}
int main()
{
std::cout << std::boolalpha
<< decay_equiv<const int, int>::value << std::endl // cv-qualifiers are dropped (pass-by-value)
<< decay_equiv<const int[2], int*>::value << std::endl; // cv-qualifiers here CANNOT be dropped, they're part of the type even if passed by value
const int myConstValue = 55;
function1(myConstValue);
const int myArrayToConstValues[2] = {4,2};
function2(myArrayToConstValues);
return 0;
}
http://ideone.com/AW6TJS
In your example you're asking for a constant return value (you can't modify the address of the first element) but asking in the trailing return type for a non-const one, that's why the compiler is complaining and what I just wrote is the reason why the const cannot be dropped by std::decay(): it is part of the type even in a pass-by-value situation (e.g. function2()).

pass reference to array in C++

Can any one help me understand the following code
#include <iostream>
void foo(const char * c)
{
std::cout << "const char *" << std::endl;
}
template <size_t N>
void foo(const char (&t) [N])
{
std::cout << "array ref" << std::endl;
std::cout << sizeof(t) << std::endl;
}
int main()
{
const char t[34] = {'1'};
foo(t);
char d[34] = {'1'};
foo(d);
}
The output is
const char *
array ref
34
Why does the first foo calls the const char * version ? How can I make it call the reference version ?
Conversion of const char[N] to const char* is considered an "exact match" (to make literals easier, mainly), and between two exact matches a non-template function takes precedence.
You can use enable_if and is_array to force it to do what you want.
A messy way to force it might be:
#include <iostream>
template <typename T>
void foo(const T* c)
{
std::cout << "const T*" << std::endl;
}
template <typename T, size_t N>
void foo(const T (&t) [N])
{
std::cout << "array ref" << std::endl;
}
int main()
{
const char t[34] = {'1'};
foo(t);
char d[34] = {'1'};
foo(d);
}
/*
array ref
array ref
*/
I realise that the OP had char not some generic T, but nonetheless this demonstrates that the problem lay in one overload being a template and not the other.
Let's look at this modified example with no template.
void foo(const char * c)
{
std::cout << "const char *" << std::endl;
}
void foo(const char (&t) [34])
{
std::cout << "const char (&) [34]" << std::endl;
}
int main()
{
const char t[34] = {'1'};
foo(t);
}
My compiler says call of overloaded foo is ambiguous. This is because conversions from array to pointer are considered an "Exact" conversion sequence and are not better than the null conversion sequence for overload resolution (Standard section 13.3.3.1.1.)
In the original code, the template parameter N can be deduced as 34, but then both non-template foo(const char*) and foo<34>(const char (&)[34]) are considered in overload resolution. Since neither is better than the other by conversion rules, the non-template function beats the template function.
Fixing things seems tricky. It seems like the is_array template from header <type_traits> (from C++0x if possible or Boost if not) might help.
This appears to be different for various compilers.
Mircosoft and Borland both use the const char* version, while GNU is giving the output you described.
Here is a snippet from the C++ standard:
14.8.2.1 Deducing template arguments from a function call
[temp.deduct.call]
Template argument deduction is done by
comparing each function template
parameter type (call it P) with the
type of the corresponding argument of
the call (call it A) as described
below.
If P is not a reference type:
-- If A is an array type, the pointer type produced by the array-to-pointer
standard conversion (4.2) is used in
place of A for type deduction;
otherwise,
-- If A is a function type, the pointer type produced by the
function-to-pointer standard
conversion (4.3) is used in place of A
for type deduction; otherwise,
-- If A is a cv-qualified type, the top level cv-qualifiers of A's type
are ignored for type deduction.
If P is a cv-qualified type, the top
level cv-qualifiers of P's type are
ignored for type deduction. If P is a
reference type, the type referred to
by P is used for type deduction
The compiler will build an A list as follows:
Argument: t d
A: char const[34] char[34]
And parameter list P:
Parameter: c t
P: char const* char const& t[N]
By default the compiler should choose non-referenced parameters. GNU is dong it wrong the second time for some reason.

C-Style Strings as template arguments? [duplicate]

This question already has answers here:
Passing a string literal as a type argument to a class template
(17 answers)
Closed 6 years ago.
Can C-Style strings be used as template arguments?
I tried:
template <char *str>
struct X
{
const char *GetString() const
{
return str;
}
};
int main()
{
X<"String"> x;
cout<<x.GetString();
}
And although I get no complaints about the class definition, the instantiation yields 'X' : invalid expression as a template argument for 'str' (VC).
A string literal cannot be used as a template argument.
Update: Nowadays, a few years after this question was asked and answered, it is possible to use string literals as template arguments. With C++11, we can use characters packs as template arguments (template<char ...c>) and it is possible to pass a literal string to such a template.
This would work, however:
template <char const *str>
struct X
{
const char *GetString() const
{
return str;
}
};
char global_string[] = "String";
int main()
{
X<global_string> x;
cout<<x.GetString();
}
Sorry to post on such an old question, but here's what I feel is the cleanest approach to actually pass a literal as the argument without using storage.
Encode the string as a type:
template <char... chars>
using tstring = std::integer_sequence<char, chars...>;
Create a user defined literal operator:
template <typename T, T... chars>
constexpr tstring<chars...> operator""_tstr() { return { }; }
And use partial specialization to recover the character data as needed:
template <typename>
struct X;
template <char... elements>
struct X<tstring<elements...>> {
const char* GetString() const
{
static constexpr char str[sizeof...(elements) + 1] = { elements..., '\0' };
return str;
}
};
This allows you to write:
X<decltype("my_string"_tstr)>
The user defined literal uses non-standard (n3599) functionality not in C++14 but that is supported by recent GCC and Clang builds, and hopefully will be reconsidered for C++1z.
I known, this topic is a bit old but I put this comment if anyone is interested. I achieved templates with passing literal string as argument with combination of MACROS.
I made a code example,
#include <stdio.h>
#include <iostream>
#include <vector>
#include <memory>
#include <string.h>
using namespace std;
#define MAX_CONST_CHAR 100
#define MIN(a,b) (a)<(b)?(a):(b)
#define _T(s)\
getChr(s,0),\
getChr(s,1),\
getChr(s,2),\
getChr(s,3),\
getChr(s,4),\
getChr(s,5),\
getChr(s,6),\
getChr(s,7),\
getChr(s,8),\
getChr(s,9),\
getChr(s,10),\
getChr(s,11),\
getChr(s,12),\
getChr(s,13),\
getChr(s,14),\
getChr(s,15),\
getChr(s,16),\
getChr(s,17),\
getChr(s,18),\
getChr(s,19),\
getChr(s,20),\
getChr(s,21),\
getChr(s,22),\
getChr(s,23),\
getChr(s,24),\
getChr(s,25),\
getChr(s,26),\
getChr(s,27),\
getChr(s,28),\
getChr(s,29),\
getChr(s,30),\
getChr(s,31),\
getChr(s,32),\
getChr(s,33),\
getChr(s,34),\
getChr(s,35),\
getChr(s,36),\
getChr(s,37),\
getChr(s,38),\
getChr(s,39),\
getChr(s,40),\
getChr(s,41),\
getChr(s,42),\
getChr(s,43),\
getChr(s,44),\
getChr(s,45),\
getChr(s,46),\
getChr(s,47),\
getChr(s,48),\
getChr(s,49),\
getChr(s,50),\
getChr(s,51),\
getChr(s,52),\
getChr(s,53),\
getChr(s,54),\
getChr(s,55),\
getChr(s,56),\
getChr(s,57),\
getChr(s,58),\
getChr(s,59),\
getChr(s,60),\
getChr(s,61),\
getChr(s,62),\
getChr(s,63),\
getChr(s,64),\
getChr(s,65),\
getChr(s,66),\
getChr(s,67),\
getChr(s,68),\
getChr(s,69),\
getChr(s,70),\
getChr(s,71),\
getChr(s,72),\
getChr(s,73),\
getChr(s,74),\
getChr(s,75),\
getChr(s,76),\
getChr(s,77),\
getChr(s,78),\
getChr(s,79),\
getChr(s,80),\
getChr(s,81),\
getChr(s,82),\
getChr(s,83),\
getChr(s,84),\
getChr(s,85),\
getChr(s,86),\
getChr(s,87),\
getChr(s,88),\
getChr(s,89),\
getChr(s,90),\
getChr(s,91),\
getChr(s,92),\
getChr(s,93),\
getChr(s,94),\
getChr(s,95),\
getChr(s,96),\
getChr(s,97),\
getChr(s,98),\
getChr(s,99),\
getChr(s,100)
#define getChr(name, ii) ((MIN(ii,MAX_CONST_CHAR))<sizeof(name)/sizeof(*name)?name[ii]:0)
template <char... Chars_>
class E {
public:
string *str;
E(){
std::vector<char> vec = {Chars_...};
str = new string(vec.begin(),vec.end());
}
~E()
{
delete str;
}
};
int main(int argc, char *argv[])
{
E<_T("Any template can pass const strings literals")> e;
printf("%s",e.str->c_str());
}
This works with g++ 4.6 and passing argument -std=c++0x, and have a limit of 100 char but, of course, can be as greater as you want. Maybe this technique is not well optimized, but it will be more productive than declare the needed external variables (I'm sure ;) )
Constraints: The literal string must be one and last argument of the template due the passing of variadics arguments.
EDIT: Thanks to Padek he tested that this piece of code also it works with Visual Studio 2017 but changing strlen by sizeof(name)/sizeof(*name).
No, you can't work with string literals at compile time. The best you can get are the weird multicharacter literals (e.g. 'abcd') which some compile-time parsers use. They are mentioned in §2.13.2.1:
An ordinary character literal that
contains more than one c-char is a
multicharacter literal. A multicharac-
ter literal has type int and
implementation-defined value.
In C++0x there might be ways around this limitations though with the new string literals, Arctic Interactive has an interesting article on that.
With C++11 you can fairly sanely represent string literals as variadic template arguments, i.e. a collection of int template parameters. I've put together a proof of concept example that sets up one such template without manually having to write foo<16, 73, 51 ...> for every such string.
Example:
// The template we want to pass a string to
template <int... Args>
struct foo {
// It needs one helper function for decltype magic, this could be avoided though
template <int N>
static foo<N, Args...> add_one();
};
// This is the string we want to use with foo, simulating foo<"Hello world!" __FILE__>:
constexpr const char *teststr = "Hello world!" __FILE__;
// Get char N of a string literal
constexpr int strchr(const char *str, int N) { return str[N]; }
// recursive helper to build the typedef from teststr
template <int N, int P=0>
struct builder {
typedef typename builder<N, P+1>::type child;
typedef decltype(child::template add_one<strchr(teststr,P)>()) type;
};
template <int N>
struct builder<N,N> {
typedef foo<strchr(teststr, N)> type;
};
// compile time strlen
constexpr int slen(const char *str) {
return *str ? 1 + slen(str+1) : 0;
}
int main() {
builder<slen(teststr)>::type test;
// compile error to force the type to be printed:
int foo = test;
}
You'll need at least gcc 4.6 for constexpr and it could use some polish still but the compiler error I get indicates the type is being built sanely:
error: cannot convert ‘builder<19>::type {aka foo<72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 115, 108, 105, 116, 46, 99, 99, 0>}’ to ‘int’ in initializatio
No. According to C++ 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 .
Strings are not in the list.
You can use address of string with external linkage as a template parameter, e.g.:
template <const char** T> class My {
public:
void do_stuff() {
std::cout << "zzz";
}
};
const char* str;
int main()
{
My<&str> zz;
zz.do_stuff();
printf("Result: %d %d \n", 60 % 10 + 1, (60 % 10 ) + 1 );
}
C++ does not know about strings. It only knowns about "arrays of characters" and there the literal would be the pointer to the array. So if you would use the "value" of your string as template parameter you would actually use the pointer value.