'c++ constexpr concatenate char*
Context:
In my company we generate a lot of types based on IDL files. Some of the types require special logic so they are handcoded but follow the same pattern as the generated ones. We have a function which all types must implement which is a name function. This will return the type name as a char* string and the function is constexpr.
Problem:
The problem is regarding collections which could contain other collections nested potentially N number of times. I therefore am trying to concatenate two or more char* strings at compile time.
Pseudocode of what I want to achieve:
template <typename T>
constexpr char* name()
{
    constexpr char* collectionName = "std::vector";
    constexpr char* containedTypeName = name<T>();
    return concat(collectionName, "<", containedTypeName, ">");
}
Note:
There are examples out there which does something like this but is done with char[] or the use of static variables.
The question:
How can I make a constexpr function which return a char* which consists of two or more concatenated char* strings at compile time? I am bound to C++17.
Solution 1:[1]
From constexpr you cannot return char* which is constructed there... You must return some compile time known(also its size) constant thingy. A possible solution could be something like:
#include <cstring>
// Buffer to hold the result
struct NameBuffer
{
    // Hardcoded 128 bytes!!!!! Carefully choose the size!
    char data[128];
};
// Copy src to dest, and return the number of copied characters
// You have to implement it since std::strcpy is not constexpr, no big deal.
constexpr int constexpr_strcpy(char* dest, const char* src);
//note: in c++20 make it consteval not constexpr
template <typename T>
constexpr NameBuffer name()
{
    // We will return this
    NameBuffer buf{};
    constexpr const char* collectionName = "std::vector";
    constexpr const char* containedTypeName = "dummy";
    // Copy them one by one after each other
    int n = constexpr_strcpy(buf.data, collectionName);
    n += constexpr_strcpy(buf.data + n, "<");
    n += constexpr_strcpy(buf.data + n, containedTypeName);
    n += constexpr_strcpy(buf.data + n, ">");
    // Null terminate the buffer, or you can store the size there or whatever you want
    buf.data[n] = '\0';
    return buf;
}
And since the returned char* is only depends on the template parameter in your case, you can create templated variables, and create a char* to them, and it can act like any other char*...
EDIT:
I have just realized that your pseudo code will never work!! Inside name<T>() you are trying to call name<T>().
You must redesign this!!! But! With some hack you can determine the size at compile time somehow for example like this:
#include <cstring>
#include <iostream>
template<std::size_t S>
struct NameBuffer
{
    char data[S];
};
// Copy src to dest, and return the number of copied characters
constexpr int constexpr_strcpy(char* dest, const char* src)
{
    int n = 0;
    while((*(dest++) = *(src++))){ n++; }
    return n;
}
// Returns the len of str without the null term
constexpr int constexpr_strlen(const char* str)
{
    int n = 0;
    while(*str) { str++; n++; }
    return n;
}
// This template parameter does nothing now...
// I left it there so you can see how to create the template variable stuff...
//note: in c++20 make it consteval not constexpr
template <typename T>
constexpr auto createName()
{
    constexpr const char*  collectionName = "std::vector";
    constexpr const char* containedTypeName = "dummy";
    constexpr std::size_t buff_size = constexpr_strlen(collectionName) + 
                                      constexpr_strlen(containedTypeName) +
                                      2; // +1 for <, +1 for >
    /// +1 for the nullterm
    NameBuffer<buff_size + 1> buf{};
    /// I'm lazy to rewrite, but now we already calculated the lengths...
    int n = constexpr_strcpy(buf.data, collectionName);
    n += constexpr_strcpy(buf.data + n, "<");
    n += constexpr_strcpy(buf.data + n, containedTypeName);
    n += constexpr_strcpy(buf.data + n, ">");
    buf.data[n] = '\0';
    return buf;
}
// Create the buffer for T
template<typename T>
static constexpr auto name_buff_ = createName<T>();
// point to the buffer of type T. It can be a function too as you wish
template<typename T>
static constexpr const char* name = name_buff_<T>.data;
int main()
{
    // int is redundant now, but this is how you could use this
    std::cout << name<int> << '\n';
    return 0;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source | 
|---|---|
| Solution 1 | 
