c++ numerical parser using template metaprogramming - c++

i have been working for around 4 hours tryng to find a way that this code compile:
template < char ... RHS, unsigned int i>
struct t {
static const char s[] = t<' ', char(i+'0'), RHS, i-1>::s;
};
template <char ... RHS >
struct t<RHS, 0> {
static const char s[] = {'0', RHS, '\0'};
};
void main() {
std::cout << t<5>::s; // {'0',' ','1',' ','2',' ','3',' ','4',' ','5','\0'}
}
i from another post, that i dont have the link, but this code is tryng to parser a number to char at compile time. any help why this code is not compiling?
thx in advance!

#include <iostream>
// template parameter pack needs to at the end
template < unsigned int i, char ... RHS >
struct t {
// can't copy-initialize an array from another array
constexpr static char const* s()
{ return t<i-1, ' ', char(i+'0'), RHS...>::s(); };
};
template <char ... RHS >
struct t<0, RHS...> {
// can't initialize a const array inside the class,
// need to use `constexpr`
constexpr static char arr[] = {'0', RHS..., '\0'};
constexpr static char const* s()
{ return arr; }
};
// need to define the array variable, it's ODR-used
template <char ... RHS >
constexpr char t<0, RHS...>::arr[];
int main() {
std::cout << t<5>::s(); // {'0',' ','1',' ','2',' ','3',' ','4',' ','5','\0'}
}
And here's a version with "minimal changes":
#include <iostream>
#include <array>
template < unsigned int i, char ... RHS >
struct t {
constexpr static std::array<char, sizeof...(RHS)+2*i+2> s
= t<i-1, ' ', char(i+'0'), RHS...>::s;
};
template < unsigned int i, char ... RHS >
constexpr std::array<char, sizeof...(RHS)+2*i+2> t<i, RHS...>::s;
template <char ... RHS >
struct t<0, RHS...> {
constexpr static std::array<char, sizeof...(RHS)+2> s
= {{'0', RHS..., '\0'}};
};
template <char ... RHS >
constexpr std::array<char, sizeof...(RHS)+2>
t<0, RHS...>::s;
int main() {
std::cout << t<5>::s.data();
}
Note how the array is copied into each class. The most-derived ("top-level") array is odr-used via .data(), so definition of s for the primary template is necessary. The definition of s for the specialization isn't required here.
Instead of using a static data member, you could also construct the array inside a constexpr function:
constexpr static std::array<char, sizeof...(RHS)+2> arr()
{ return {{'0', RHS..., '\0'}}; }
The drawback is that this returned array has automatic lifetime, so you can't pass its .data() to the base classes.

Here is something similar that will create a string. For example, Stringer<7> will create the string "0 1 2 3 4 5 6 7".
template <uint32_t i>
struct Stringer
{
string str = Stringer<i - 1>().str + " " + to_string(i);
};
template <>
struct Stringer<0>
{
string str = "0";
};
int main(int argc, const char *argv[])
{
cout << Stringer<7>().str << endl;
return 0;
}

Related

pass char array properly to templated method

I have some class with enum variable and I wants to stringify that enum. For that reason I added typeValue with stringified values. Also I added separate class for getting stringified values but I can't pass variables in correct way.
Code like this:
#include <iostream>
#include <string>
#include <algorithm>
#define stringify(name) #name
struct MyClass
{
enum class TYPE
{
UNKNOWN = 0,
CLIENT,
SERVER
};
MyClass(TYPE t)
:_type(t)
{
}
TYPE type()
{
return _type;
}
inline static const char* typeValue[10] =
{
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
private:
TYPE _type;
};
struct EnumStringify
{
/**
* Get enum type string by value
*/
template <class EnumType>
static std::string getEnumTypeName(const EnumType& t, const char (*typeValues)[10])
{
std::string s(typeValues[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
};
int main()
{
MyClass a(MyClass::TYPE::CLIENT);
std::cout << EnumStringify::getEnumTypeName<MyClass::TYPE>(a.type(), MyClass::typeValue).c_str();
}
Errors happened:
main.cpp:55:90: error: no matching function for call to ‘EnumStringify::getEnumTypeName(MyClass::TYPE, const char* [10])’
std::cout << EnumStringify::getEnumTypeName<MyClass::TYPE>(a.type(), MyClass::typeValue).c_str();
^
main.cpp:45:21: note: candidate: template static std::string EnumStringify::getEnumTypeName(const EnumType&, const char (*)[10])
static std::string getEnumTypeName(const EnumType& t, const char (*typeValues)[10])
^~~~~~~~~~~~~~~
main.cpp:45:21: note: template argument deduction/substitution failed:
main.cpp:55:90: note: cannot convert ‘MyClass::typeValue’ (type ‘const char* [10]’) to type ‘const char (*)[10]’
std::cout << EnumStringify::getEnumTypeName<MyClass::TYPE>(a.type(), MyClass::typeValue).c_str();
Please help me to make it correct. Maybe with typedef would be better?
Two issues:
const char(*name)[] defines a pointer to an array of const chars, not strings.
MyClass::typeValue is of type const char* (&)[10] so it cannot be implicitly converted to a pointer to that array.
This works:
class MyClass{
//...
constexpr static const char* typeValue[10] = {
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
private:
TYPE _type;
};
struct EnumStringify
{
/**
* Get enum type string by value
*/
template <class EnumType>
static std::string getEnumTypeName(const EnumType& t, const char* const (&typeValues )[10])
{
std::string s(typeValues[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
};
I also added constexpr and pass the value by const reference since you do not want to change it.
My advice would to be use std::array, it alleviates all the issues with passing arrays around.
std::array solution
#include <iostream>
#include <string>
#include <algorithm>
#include <array>
#define stringify(name) #name
struct MyClass
{
enum class TYPE
{
UNKNOWN = 0,
CLIENT,
SERVER
};
MyClass(TYPE t)
:_type(t)
{
}
TYPE type()
{
return _type;
}
constexpr static auto typeValue = std::array{
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
private:
TYPE _type;
};
struct EnumStringify
{
template <class EnumType, class Array>
static std::string getEnumTypeName(const EnumType& t, const Array& array)
{
std::string s(array[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
};
int main()
{
MyClass a(MyClass::TYPE::CLIENT);
std::cout << EnumStringify::getEnumTypeName(a.type(), MyClass::typeValue).c_str();
}
Template variable
If I were to write such code, I would use template variable which the user can specialize to provide strings for their own enums:
//
// "Stringify Library"
#define stringify(name) #name
struct MissingEnumTable{};
template<class Enum>
constexpr MissingEnumTable stringified_enum{};
template <class E>
static std::string getEnumTypeName(const E& t)
{
static_assert(!std::is_same_v<decltype(stringified_enum<E>),MissingEnumTable>,
"Enum E is missing stringified_enum table specialization.");
std::string s(stringified_enum<E>[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
// END
// Library user
// - Provide strings for a custom array
enum class TYPE
{
UNKNOWN = 0,
CLIENT,
SERVER
};
template<>
constexpr auto stringified_enum<TYPE> = std::array{
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
int main()
{
std::cout << getEnumTypeName(TYPE::UNKNOWN).c_str();
}

A way to get parameter pack from tuple / array?

So, I'm attempting to mess with constexpr strings as one will do and really only have this thus far:
template<char... CS> struct text {
static constexpr char c_str[] = {CS...};
static constexpr int size = sizeof...(CS);
};
and so this compiles
text<'a','b','c'> t;
std::cout<< t.c_str <<std::endl;
and outputs 'abc' as expected.
What I'm wondering is if there's a non-convoluted way to do the reverse; have a function that returns a text type with the necessary char template arguments given a char array.
Not exactly what you asked... and a little convoluted, I suppose... but if you define a constexpr function to detect the length of a string
constexpr std::size_t strLen (char const * str, std::size_t len = 0U)
{ return *str ? strLen(++str, ++len) : len; }
and an helper struct that define the required type
template <char const *, typename>
struct foo_helper;
template <char const * Str, std::size_t ... Is>
struct foo_helper<Str, std::index_sequence<Is...>>
{ using type = text<Str[Is]...>; };
you can obtain your type passing the string to
template <char const * Str>
struct foo : public foo_helper<Str, std::make_index_sequence<strLen(Str)>>
{ };
Unfortunately you can't pass a string literal to it in this way
foo<"abc">::type
but you have to pass from a global variable
constexpr char abcVar[] = "abc";
and call foo using the global variable
foo<abcVar>::type
This solution uses std::index_sequence and std::make_index_sequence, available only starting from C++14, but isn't too difficult to write a substitute for they in C++11.
The following is a full working example
#include <utility>
#include <iostream>
#include <type_traits>
template <char ... CS>
struct text
{
static constexpr char c_str[] = {CS...};
static constexpr int size = sizeof...(CS);
};
constexpr std::size_t strLen (char const * str, std::size_t len = 0U)
{ return *str ? strLen(++str, ++len) : len; }
template <char const *, typename>
struct foo_helper;
template <char const * Str, std::size_t ... Is>
struct foo_helper<Str, std::index_sequence<Is...>>
{ using type = text<Str[Is]...>; };
template <char const * Str>
struct foo : public foo_helper<Str, std::make_index_sequence<strLen(Str)>>
{ };
constexpr char abcVar[] = "abc";
int main()
{
static_assert(std::is_same<foo<abcVar>::type,
text<'a', 'b', 'c'>>{}, "!");
}
Off Topic: I suggest to add an ending zero in c_str[]
static constexpr char c_str[] = {CS..., 0};
if you want use it as the c_str() method of std::string.

Converting aggregate initialization of a struct containing a CONST char array[N] member into a constructor

Given a simple struct containing a const char array, this can be easily initialized via aggregate initialization:
struct int_and_text {
constexpr static int Size = 8;
const int i;
const char text[Size];
};
const int_and_text some_data[] = { { 0, "abc"}, { 77, "length8" } };
But now I want to add a constructor, but so far nothing I tried worked, even one using a constexpr memcpy-ish variant.
template <std::size_t N>
int_and_text(int i_, const char (&text_)[N])
: i{i_},
text{" ? " /* const text[8] from const text[1-8] */ }
{ static_assert(N <= Size); }
Is this possible? A const char text_[8] constructor argument seems to decay into a char*. In the long term making everything constexpr would be nice as well.
#include <cstddef>
#include <utility>
class int_and_text
{
public:
template <std::size_t N>
int_and_text(int i_, const char (&text_)[N])
: int_and_text(i_, text_, std::make_index_sequence<N>{})
{
}
private:
template <std::size_t N, std::size_t... Is>
int_and_text(int i_, const char (&text_)[N], std::index_sequence<Is...>)
: i{i_}
, text{text_[Is]...}
{
static_assert(N <= Size);
}
constexpr static int Size = 8;
const int i;
const char text[Size];
};
const int_and_text some_data[] = { {0, "abc"}, {77, "length8"} };
DEMO

Concatenate compile-time strings in a template at compile time?

Currently I have:
template <typename T> struct typename_struct<T*> {
static char const* name() {
return (std::string(typename_struct<T>::name()) + "*").c_str();
}
};
I wonder if I can avoid the whole bit where I'm forced to allocate a string to perform the concatenation.
This is all happening at compile time, i.e. I intend to get the string "int****" when I reference typename_struct<int****>::name(). (Do assume that I have declared a corresponding specialization for int which returns "int")
As the code is written now, does the compiler do the concatenation with std::string during compile time only? (I would be okay with that) Or does such a call result in 4 std::string based concatenations at runtime? (I would not be okay with that)
You could use something like this. Everything happens at compile time. Specialize base_typename_struct to define your primitive types.
template <const char* str, int len, char... suffix>
struct append {
static constexpr const char* value() {
return append<str, len-1, str[len-1], suffix...>::value();
}
};
template <const char* str, char... suffix>
struct append<str, 0, suffix...> {
static const char value_str[];
static constexpr const char* value() {
return value_str;
}
};
template <const char* str, char... suffix>
const char append<str, 0, suffix...>::value_str[] = { suffix..., 0 };
template <typename T>
struct base_typename_struct;
template <>
struct base_typename_struct<int> {
static constexpr const char name[] = "int";
};
template <typename T, char... suffix>
struct typename_struct {
typedef base_typename_struct<T> base;
static const char* name() {
return append<base::name, sizeof(base::name)-1, suffix...>::value();
}
};
template <typename T, char... suffix>
struct typename_struct<T*, suffix...>:
public typename_struct<T, '*', suffix...> {
};
int main() {
cout << typename_struct<int****>::name() << endl;
}
Alternative way without using recursive templates (but requires C++14):
#include <utility>
template<int...I> using is = std::integer_sequence<int,I...>;
template<int N> using make_is = std::make_integer_sequence<int,N>;
constexpr auto size(const char*s) { int i = 0; while(*s!=0){++i;++s;} return i; }
template<const char*, typename, const char*, typename>
struct concat_impl;
template<const char* S1, int... I1, const char* S2, int... I2>
struct concat_impl<S1, is<I1...>, S2, is<I2...>> {
static constexpr const char value[]
{
S1[I1]..., S2[I2]..., 0
};
};
template<const char* S1, const char* S2>
constexpr auto concat {
concat_impl<S1, make_is<size(S1)>, S2, make_is<size(S2)>>::value
};
Example:
constexpr const char a[] = "int";
constexpr const char c[] = "**";
#include <iostream>
int main()
{
std::cout << concat<a,b> << '\n';
}
append characters to string can also be implemented like this, by replacing the second const char* parameter with char....
I'm not sure of what you're searching for but I believe you're interested in a combination of typeid and name-demangling (which compiler are you using?)
In gcc it would be something like
#include<iostream>
#include <string>
#include <typeinfo>
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
using namespace std;
std::string demangle(const char* name) {
int status = -4; // some arbitrary value to eliminate the compiler warning
// enable c++11 by passing the flag -std=c++11 to g++
std::unique_ptr<char, void(*)(void*)> res {
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status==0) ? res.get() : name ;
}
template <typename T> struct typename_struct {
static std::string name() {
std::string typeName = typeid(T).name();
return demangle(typeName.c_str());
}
};
int main(){
cout << typename_struct<int****>::name(); // Prints "int****"
return 0;
}
http://ideone.com/nLsFF0
Sources: https://stackoverflow.com/a/4541470/1938163
As for your question: those aren't constexpr constructs, thus the evaluation happens at runtime although the templated parameters and code are instantiated at compile-time.
Using templates doesn't mean every instruction contained in there will be executed and resolved at "compile-time".
I believe you can't achieve this whole bunch of stuff at compile-time since there are de-mangling functions (ABI-specific) involved. If I interpreted your question wrong please let me know.
#include <iostream>
//
***************************************************************************
template<const char* S1, const char* S2, size_t I1 = 0, size_t I2 = 0, char = S1[I1], char = S2[I2], char... Chars>
struct Concat : Concat<S1, S2, I1 + 1, I2, S1[I1 + 1], S2[I2], Chars..., S1[I1]>
{
};
// ****************************************************************************
template<const char* S1, const char* S2, size_t I1, size_t I2, char C2, char... Chars>
struct Concat<S1, S2, I1, I2, 0, C2, Chars...> : Concat<S1, S2, I1, I2 + 1, 0, S2[I2 + 1], Chars..., S2[I2]>
{
};
// ****************************************************************************
template<const char* S1, const char* S2, size_t N1, size_t N2, char... Chars>
struct Concat<S1, S2, N1, N2, 0, 0, Chars...>
{
static constexpr const char Text[] = { Chars... , 0 };
};
// ****************************************************************************
static constexpr const char A[] = "123";
static constexpr const char B[] = "456";
// ****************************************************************************
int main(int argc, char* argv[]){
std::cout << Concat<A, B>::Text << std::endl;
return 0;
}

C++: How to use type in template function to branch?

I am not quite proficient with templates. How do I write the a template function called get that chooses the array it gets from based on the template type? See the example below:
struct Foo
{
int iArr[10];
char cArr[10];
// How to pick array here based on template type?
template < typename T >
T get( int idx )
{
// This does NOT work!
switch ( T )
{
case int:
return iArr[ idx ];
case char:
return cArr[ idx ];
}
}
};
// Expected behaviour of get()
Foo foo;
int i = foo.get< int >( 2 );
char c = foo.get< char >( 4 );
While the solution proposed by Jason works, it's far from idiomatic, and is harder to maintain since the case values in the switch statement 1) have no apparent meaning ("magic numbers") and 2) could easily get out of sync with the values in the switch_value<> specializations. I would propose this instead:
struct Foo {
Foo() : iArr(), cArr() { }
template<typename T>
T get(std::size_t const idx) const {
return Foo::get_dispatcher<T>::impl(*this, idx);
}
private:
int iArr[10];
char cArr[10];
template<typename T>
struct get_dispatcher;
};
template<>
struct Foo::get_dispatcher<int> {
static int impl(Foo const& foo, std::size_t const idx) {
return foo.iArr[idx];
}
};
template<>
struct Foo::get_dispatcher<char> {
static char impl(Foo const& foo, std::size_t const idx) {
return foo.cArr[idx];
}
};
Invoking Foo::get<> with any type other than int or char will yield a compiler error.
You would need to add a value structure of some type you can use to get the values for your switch-statement from. For instance:
template<typename T>
struct switch_value {};
template<>
struct switch_value<int>
{
enum { value = 1 };
};
template<>
struct switch_value<char>
{
enum { value = 2 };
};
//then inside you structure Foo
template <typename T>
T get( int idx )
{
switch ( switch_value<T>::value )
{
case 1:
return iArr[ idx ];
case 2:
return cArr[ idx ];
}
}
The nice thing here is this should throw a compiler error if you use a type that does not have a valid value since the default version of switch_value<T> does not define a member value, so if you haven't specialized the structure for a specific type, then such a template instantiation will fail.
All of these answers are wayyyy overkill.
template <typename T>
T get( int idx )
{
if ( boost::is_same<T, int>::value)
return *(T*)&iArr[ idx ];
else
return *(T*)&cArr[ idx ];
}
You could specialise the member function:
struct foo
{
int iArr[10];
char cArr[10];
template<typename T>
T &get(int ipos) { assert( false && "Foo.get: Invalid type!" ); return T(); }
template<>
int &get<int>(int ipos) { return iArr[ipos]; }
template<>
char &get<char>(int ipos) {return cArr[ipos]; }
// add more specialisations for any other types...
};
Works with msvc++ 2010. Hope this helps.
The switch specialisation suggested by Jason should also work fine.
This is probably overkill for your example, but if you really only need to store one array at a time, then you can use the boost::variant class and a visitor, e.g.,
#include <boost/variant.hpp>
#include <iostream>
template< typename T >
class GetVisitor : public boost::static_visitor<T>
{
public:
GetVisitor(int index) : index_(index) {};
template <typename U >
T operator() (U const& vOperand) const
{
return vOperand[index_];
}
private:
int index_;
};
int main ()
{
int iArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
char cArr[10] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' };
boost::variant<int*, char*> intVariant(iArr); //- assign integer array to variant
boost::variant<int*, char*> charVariant(cArr); //- assign character array to another variant
int testInt = boost::apply_visitor(GetVisitor<int>(2), intVariant);
char testChar = boost::apply_visitor(GetVisitor<char>(9), charVariant);
std::cout << "returned integer is " << testInt << std::endl;
std::cout << "returned character is " << testChar << std::endl;
return 0;
}
output is:
returned integer is 3
returned character is j
The restriction on the variant implied by the GetVisitor class is that all members of the variant must implement:
T operator[](int)
So you could also add, e.g., std::vector and std::deque as potential members of the variant.
I assume this is what you want beside just focusing on template function:
in a .h file
template < typename T >
struct Foo
{
T arr[10];
T get( int idx )
{
return arr[ idx ];
}
};
somewhere you use it like:
Foo<int> foo1;
Foo<char> foo2;
int i = foo1.get( 2 );
char c = foo2.get( 4 );