时间:2019-03-08 标签:c++constexprconcatenatechar*

c++ constexpr concatenate char*

上下文: 在我的公司,我们基于 IDL 文件生成了很多类型。有些类型需要特殊的逻辑,因此它们是手工编码的,但遵循与生成的类型相同的模式。我们有一个所有类型都必须实现的函数,它是一个名称函数。这将 return 类型名称作为 char* 字符串,函数是 constexpr.

问题: 问题是关于可能包含嵌套 N 次的其他集合的集合。因此,我试图在编译时 连接两个或多个 char* 字符串 .

我想要实现的伪代码:

template <typename T>
constexpr char* name()
{
    constexpr char* collectionName = "std::vector";
    constexpr char* containedTypeName = name<T>();
    return concat(collectionName, "<", containedTypeName, ">");
}

注: 有一些例子可以做这样的事情,但是是用 char[] 或使用静态变量完成的。

问题: 我如何制作一个 constexpr 函数,其中 return 一个 char* 在编译时由两个或多个连接的 char* 字符串组成?我绑定到 C++17。

从 constexpr 你不能 return 在那里构造的 char*...你必须 return 一些已知的编译时间(还有它的大小)常量。 一个可能的解决方案可能是这样的:

#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] = '[=10=]';
    return buf;
}

Demo

并且由于 returned char* 仅取决于您的情况下的模板参数,您可以创建模板化变量,并为它们创建一个 char*,它可以像任何其他 char* ...

编辑:

我刚刚意识到你的伪代码永远行不通!!在 name<T>() 中,您正在尝试调用 name<T>().
你必须重新设计这个!!!但!通过一些技巧,您可以在编译时以某种方式确定大小,例如:

#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] = '[=11=]';
    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;
}

Demo