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

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

Related

How to compose a string literal from constexpr char arrays at compile time?

I'm trying to create a constexpr function that concatenates const char arrays to one array. My goal is to do this recursively by refering to a specialized variable template join that always joins two const char*'s . But the compiler doesn't like it and throws an error message that I can't get behind.
I've already checked out this topic but it unfortunately doesn't have a straight up answer.
Code:
#include <type_traits>
#include <cstdio>
#include <iostream>
constexpr auto size(const char*s)
{
int i = 0;
while(*s!=0) {
++i;
++s;
}
return i;
}
template <const char* S1, typename, const char* S2, typename>
struct join_impl;
template <const char* S1, int... I1, const char* S2, int... I2>
struct join_impl<S1, std::index_sequence<I1...>, S2, std::index_sequence<I2...>>
{
static constexpr const char value[]{ S1[I1]..., S2[I2]..., 0 };
};
template <const char* S1, const char* S2>
constexpr auto join
{
join_impl<S1, std::make_index_sequence<size(S1)>, S2, std::make_index_sequence<size(S2)>>::value
};
template <const char* S1, const char* S2, const char*... S>
struct join_multiple
{
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
};
template <const char* S1, const char* S2>
struct join_multiple<S1, S2>
{
static constexpr const char* value = join<S1, S2>;
};
constexpr const char a[] = "hello";
constexpr const char b[] = "world";
constexpr const char c[] = "how is it going?";
int main()
{
// constexpr size_t size = 100;
// char buf[size];
// lw_ostream{buf, size};
std::cout << join_multiple<a, b, c>::value << std::endl;
}
Error:
<source>:33:82: error: qualified name refers into a specialization of variable template 'join'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
<source>:25:16: note: variable template 'join' declared here
constexpr auto join
^
<source>:33:34: error: default initialization of an object of const type 'const char *const'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
^
= nullptr
2 errors generated.
ASM generation compiler returned: 1
<source>:33:82: error: qualified name refers into a specialization of variable template 'join'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
<source>:25:16: note: variable template 'join' declared here
constexpr auto join
^
<source>:33:34: error: default initialization of an object of const type 'const char *const'
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>::value;
^
= nullptr
2 errors generated.
Execution build compiler returned:
What am I missing?
As alternative, to avoid to build the temporary char arrays, you might work with types (char sequences) and create the char array variable only at the end, something like:
constexpr auto size(const char*s)
{
int i = 0;
while(*s!=0) {
++i;
++s;
}
return i;
}
template <const char* S, typename Seq = std::make_index_sequence<size(S)>>
struct as_sequence;
template <const char* S, std::size_t... Is>
struct as_sequence<S, std::index_sequence<Is...>>
{
using type = std::integer_sequence<char, S[Is]...>;
};
template <typename Seq>
struct as_string;
template <char... Cs1>
struct as_string<std::integer_sequence<char, Cs1...>>
{
static constexpr const char c_str[] = {Cs1..., '\0'};
};
template <typename Seq1, typename Seq2, typename... Seqs>
struct join_seqs
{
using type = typename join_seqs<typename join_seqs<Seq1, Seq2>::type, Seqs...>::type;
};
template <char... Cs1, char... Cs2>
struct join_seqs<std::integer_sequence<char, Cs1...>, std::integer_sequence<char, Cs2...>>
{
using type = std::integer_sequence<char, Cs1..., Cs2...>;
};
template <const char*... Ptrs>
const auto join =
as_string<typename join_seqs<typename as_sequence<Ptrs>::type...>::type>::c_str;
Demo
There are two issues here.
First, join is a template variable, so it does not contain the so-called value_type, it itself is a value, so your join_multiple should be
template <const char* S1, const char* S2, const char*... S>
struct join_multiple {
static constexpr const char* value = join<S1, join_multiple<S2, S...>::value>;
};
Second and less important, the integer type of index_sequence is size_t instead of int, so the partial specialization of join_impl should be (this is not necessary, but using a type other than size_t will cause GCC to reject it incorrectly)
template <const char* S1, size_t... I1, const char* S2, size_t... I2>
struct join_impl<S1, std::index_sequence<I1...>, S2, std::index_sequence<I2...>> {
static constexpr const char value[]{ S1[I1]..., S2[I2]..., 0 };
};
Demo

Generating simple format string at compile time

I'm trying to generate a simple format string for fmt at compile time, but I can't quite figure out the string concatenation. I'm limited to c++ 14.
What I'd like is to be able to generate a format string for N items, so it could be used as follows:
auto my_string = fmt::format(format_string_struct<2>::format_string, "Item", "key1", "value1", "key2", "value2");
The generated format string would be: "{} {}={} {}={}", resulting in a formatted string of "Item key1=value1 key2=value2". The base of the format string would be "{}", and it appends a " {}={}" for each N.
I'm trying to use recursive templates, but I just can't quite get everything to work. I got some code from Concatenate compile-time strings in a template at compile time? for concatenating strings (though I get a undefined reference to `concat_impl<&base_format_string_struct::format_string, std::integer_sequence<int, 0, 1>, &format_string_parameter_struct::parameter_string, std::integer_sequence<int, 0, 1, 2, 3, 4, 5, 6, 7> >::value' error)
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
};
struct base_format_string_struct {
static constexpr const char format_string[] = "{}";
};
struct format_string_parameter_struct {
static constexpr const char parameter_string[] = " {}=\"{}\"";
};
template <int arg_count, const char[]... params>
struct format_string_struct:
public format_string_struct<arg_count - 1, format_string_parameter_struct, params...> {
};
template <int arg_count>
struct format_string_struct:
public format_string_struct<arg_count - 1, format_string_parameter_struct> {
};
template <const char[]... params>
struct format_string_struct<0> {
static const char* format_string() {
return concat<base_format_string_struct, params...>;
}
};
You have several typos (and a concat limited to 2 argument).
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, const char*... Ss>
struct concat
{
constexpr static const char* value = concat_impl<S1, make_is<size(S1)>, concat<S2, Ss...>::value, make_is<size(concat<S2, Ss...>::value)>>::value;
};
template<const char* S1, const char* S2>
struct concat<S1, S2>
{
constexpr static const char* value = concat_impl<S1, make_is<size(S1)>, S2, make_is<size(S2)>>::value;
};
struct base_format_string_struct {
static constexpr const char format_string[] = "{}";
};
struct format_string_parameter_struct {
static constexpr const char parameter_string[] = " {}=\"{}\"";
};
template <int arg_count, const char*... params>
struct format_string_struct:
public format_string_struct<arg_count - 1, format_string_parameter_struct::parameter_string, params...> {
};
template <int arg_count>
struct format_string_struct<arg_count>:
public format_string_struct<arg_count - 1, format_string_parameter_struct::parameter_string> {
};
template <const char*... params>
struct format_string_struct<0, params...> {
static const char* format_string() {
return concat<base_format_string_struct::format_string, params...>::value;
}
};
Demo
But I would probably go with constexpr functions:
#if 1 // missing some constexpr for std::array/std::copy
template <typename T, std::size_t N>
struct my_array
{
T data[N];
};
template <typename CIT, typename IT>
constexpr void copy(CIT begin, CIT end, IT dest)
{
for (CIT it = begin; it != end; ++it)
{
*dest = *it;
++dest;
}
}
#endif
template <std::size_t N>
constexpr my_array<char, 2 + 8 * N + 1> format_string_struct_impl()
{
my_array<char, 2 + 8 * N + 1> res{};
constexpr char init[] = "{}"; // size == 2
constexpr char extra[] = R"( {}="{}")"; // size == 8
copy(init, init + 2, res.data);
for (std::size_t i = 0; i != N; ++i) {
copy(extra, extra + 8, res.data + 2 + i * 8);
}
return res;
}
template <std::size_t N>
constexpr my_array<char, 2 + 8 * N + 1> format_string_struct()
{
// Ensure the computation is done compile time.
constexpr auto res = format_string_struct_impl<N>();
return res;
}
Demo

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

c++ numerical parser using template metaprogramming

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