如何在编译时连接静态字符串?

时间:2016-08-15 13:24:25

标签: c++ string-concatenation

我正在尝试使用模板创建type_info::name()函数的模拟,该函数会发出const - 限定名称。例如。 typeid(bool const).name()"bool",但我希望看到"bool const"。因此,对于泛型类型,我定义:

template<class T> struct type_name { static char const *const _; };

template<class T> char const *const type_name<T>::_ = "type unknown";

char const *const type_name<bool>::_ = "bool";
char const *const type_name<int>::_ = "int";
//etc.

然后type_name<bool>::_"bool"。对于非const类型,显然我可以为每种类型添加一个单独的定义,所以char const *const type_name<bool const>::_ = "bool const";等等。但我想我会尝试部分特化和连接宏来在一行中派生任何类型的const限定名称它之前定义了非const - 限定名称。所以

#define CAT(A, B) A B

template<class T> char const *const type_name<T const>::_
    = CAT(type_name<T>::_, " const"); // line [1]

但是type_name<bool const>::_error C2143: syntax error: missing ';' before 'string'提供了line [1]。我认为type_name<bool>::_是一个在编译时已知的静态字符串,那么如何在编译时将它与" const"连接起来?

我尝试了更简单的例子,但同样的问题:

char str1[4] = "int";
char *str2 = MYCAT(str1, " const");

2 个答案:

答案 0 :(得分:6)

我最近重新审视了这个问题,发现我给出的先前答案在连接多个字符串时产生了可笑的长编译时间。

我已经开发出了一种新的解决方案,该方法利用constexpr函数来删除需要较长编译时间的递归模板。

#include <array>
#include <iostream>
#include <string_view>

template <std::string_view const&... Strs>
struct join
{
    // Helper to get a string literal from a std::array
    template <std::size_t N, std::array<char, N> const& S, typename>
    struct to_char_array;
    template <std::size_t N, std::array<char, N> const& S, std::size_t... I>
    struct to_char_array<N, S, std::index_sequence<I...>>
    {
        static constexpr const char value[]{S[I]..., 0};
    };
    // Join all strings into a single std::array of chars
    static constexpr auto impl() noexcept
    {
        constexpr std::size_t len = (Strs.size() + ... + 0);
        std::array<char, len + 1> arr{};
        auto append = [i = 0, &arr](auto const& s) mutable {
            for (auto c : s) arr[i++] = c;
        };
        (append(Strs), ...);
        arr[len] = 0;
        return arr;
    }
    // Give the joined string static storage
    static constexpr auto arr = impl();
    // Convert to a string literal, then view as a std::string_view
    static constexpr std::string_view value =
        to_char_array<arr.size(), arr, std::make_index_sequence<arr.size()>>::value;
};
// Helper to get the value out
template <std::string_view const&... Strs>
static constexpr auto join_v = join<Strs...>::value;

// Hello world example
static constexpr std::string_view hello = "hello";
static constexpr std::string_view space = " ";
static constexpr std::string_view world = "world";
static constexpr std::string_view bang = "!";
// Join them all together
static constexpr auto joined = join_v<hello, space, world, bang>;

int main()
{
    std::cout << joined << '\n';
}

即使使用大量要串联的字符串,这也可以缩短编译时间。

我个人认为此解决方案更易于遵循,因为constexpr impl函数类似于在运行时如何解决此问题。

答案 1 :(得分:1)

基于@Hededes answer,如果我们允许递归模板,则许多字符串的串联可以实现为:

#include <string_view>
#include <utility>
#include <iostream>

namespace impl
{
/// Base declaration of our constexpr string_view concatenation helper
template <std::string_view const&, typename, std::string_view const&, typename>
struct concat;

/// Specialisation to yield indices for each char in both provided string_views,
/// allows us flatten them into a single char array
template <std::string_view const& S1,
          std::size_t... I1,
          std::string_view const& S2,
          std::size_t... I2>
struct concat<S1, std::index_sequence<I1...>, S2, std::index_sequence<I2...>>
{
  static constexpr const char value[]{S1[I1]..., S2[I2]..., 0};
};
} // namespace impl

/// Base definition for compile time joining of strings
template <std::string_view const&...> struct join;

/// When no strings are given, provide an empty literal
template <>
struct join<>
{
  static constexpr std::string_view value = "";
};

/// Base case for recursion where we reach a pair of strings, we concatenate
/// them to produce a new constexpr string
template <std::string_view const& S1, std::string_view const& S2>
struct join<S1, S2>
{
  static constexpr std::string_view value =
    impl::concat<S1,
                 std::make_index_sequence<S1.size()>,
                 S2,
                 std::make_index_sequence<S2.size()>>::value;
};

/// Main recursive definition for constexpr joining, pass the tail down to our
/// base case specialisation
template <std::string_view const& S, std::string_view const&... Rest>
struct join<S, Rest...>
{
  static constexpr std::string_view value =
    join<S, join<Rest...>::value>::value;
};

/// Join constexpr string_views to produce another constexpr string_view
template <std::string_view const&... Strs>
static constexpr auto join_v = join<Strs...>::value;


namespace str
{
static constexpr std::string_view a = "Hello ";
static constexpr std::string_view b = "world";
static constexpr std::string_view c = "!";
}

int main()
{
  constexpr auto joined = join_v<str::a, str::b, str::c>;
  std::cout << joined << '\n';
  return 0;
}

我将c ++ 17与std::string_view一起使用,是因为size方法很方便,但是可以像@Hedede那样很容易地适应使用const char[]文字。

此答案旨在回答问题的标题,而不是描述的更具体的问题。