简体   繁体   English

如何在编译时创建一个带有 string_views 序列的 constexpr 数组?

[英]How to create a constexpr array with a sequence of string_views at compile time?

I would like to create a constexpr std::array<std::string_view, ConstexprNumber> .我想创建一个constexpr std::array<std::string_view, ConstexprNumber> It should for example contain constexpr std::strings_view's like this:例如,它应该包含constexpr std::strings_view's如下所示:

"text0", "text1", "text2", ..... "textn" “文本0”,“文本1”,“文本2”,......“文本”

I came up with the following initial solution:我想出了以下初始解决方案:

#include <iostream>
#include <array>
#include <utility>
#include <string>
#include <string_view>

// Number of strings that we want to generate
constexpr size_t NumberOfTextsToGenerate = 10u;

// constexpr function to build a string
constexpr std::string_view makeString(unsigned int i) {
    return std::string_view("text");
}

// Helper: constexpr function that will create an array of string_views and initialize it
template <unsigned int... ManyIntegers>
constexpr auto generateTextHelper(std::integer_sequence<unsigned int, ManyIntegers...>) {
    return std::array<std::string_view, sizeof...(ManyIntegers)>{ {makeString(ManyIntegers)...}};
}

// Helper: constexpr function that will return an array of string_views as shown above with a specified number of texts
constexpr auto generateTextArray() {
    return generateTextHelper(std::make_integer_sequence<unsigned int, NumberOfTextsToGenerate>());
}

// This is a definition of a std::array<std::string_view,UpperBound> initialized with some text
constexpr auto text = generateTextArray();

int main() {
    for (size_t i{}; i < NumberOfTextsToGenerate; ++i)
        std::cout << text[i] << '\n';
    return 0;
}

But, of course the "makeString" function does not do what I want.但是,当然“makeString” function 并没有做我想要的。 Or, better said, I do not know how to implement the correct solution.或者,更好地说,我不知道如何实施正确的解决方案。

How can we get such an array to work?我们怎样才能让这样一个数组工作? Either based on this initial solution or something similar?是基于这个初始解决方案还是类似的东西?

You can do the following:您可以执行以下操作:

constexpr size_t NumberOfTextsToGenerate = 10u;

template <std::size_t I>
struct digit_to_end_impl {
    static constexpr const char value[]{ 't', 'e', 'x', 't', (I + '0'), 0 };
};

template <std::size_t I>
struct digit_to_end {
    static constexpr std::string_view value = digit_to_end_impl<I>::value;
};

template <std::size_t I>
constexpr std::string_view makeString() {
    return digit_to_end<I>::value;
}

template <unsigned int... ManyIntegers>
constexpr auto generateTextHelper(std::integer_sequence<unsigned int, ManyIntegers...>) {
    return std::array<std::string_view, sizeof...(ManyIntegers)>{ { makeString<ManyIntegers>()... } };
}

// ...

Check out the DEMO .查看演示

However, this won't work when NumberOfTextsToGenerate is more than 10.但是,当NumberOfTextsToGenerate大于 10 时,这将不起作用。

UPDATE : Another solution through STL.更新:通过 STL 的另一种解决方案

The below solution is through boost 's preprocessing macros.下面的解决方案是通过boost的预处理宏。


Well, it took me three hours, but I still couldn't do it successfully through STL, so I gave up and eventually I went back to the macro .好吧,我花了三个小时,但我仍然无法通过STL成功,所以我放弃了,最终我回到了

If you don't want to import the whole boost library, you can just separate out these BOOST_PP macros into your project, although they are still very large, so it might take a bit of your time.如果您不想导入整个boost库,您可以将这些BOOST_PP宏分离到您的项目中,尽管它们仍然非常大,因此可能需要一些时间。

Here is the code (link to godbolt ):这是代码(链接到godbolt ):

#include <iostream>
#include <string_view>
#include <experimental/array>

#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>


int main()
{
    constexpr auto strs_array = std::experimental::make_array<std::string_view>
    (
#define ADD_STRING(z, index, data)    BOOST_PP_COMMA_IF(index) data # index
        BOOST_PP_REPEAT(20, ADD_STRING, "test")
#undef ADD_STRING
    );

    for (const std::string_view &str : strs_array) {
        std::cout << str << '\n';
    }
}

The maximum number allowed to be generated is BOOST_PP_LIMIT_REPEAT (currently is 256), which means you can currently generate up to "test255" , I guess that's enough.允许生成的最大数量是BOOST_PP_LIMIT_REPEAT (目前是 256),这意味着您目前最多可以生成"test255" ,我想这已经足够了。

Output: Output:

Program returned: 0
Program stdout
test0
test1
test2
test3
test4
test5
test6
test7
test8
test9
test10
test11
test12
test13
test14
test15
test16
test17
test18
test19

Woohoo, I finally did it!!!呜呜呜我终于成功了!!!

(Link to godbold ) (链接到上帝大胆

#include <iostream>
#include <array>
#include <utility>
#include <string>
#include <string_view>


template <class T>
using const_c_str_char_t = std::remove_const_t<std::remove_pointer_t<T>>;

template <auto str1, auto str2, size_t ...indexes1, size_t ...indexes2>
constexpr decltype(auto) string_append_sequence(std::index_sequence<indexes1...>, std::index_sequence<indexes2...>)
{
    using char_type = const_c_str_char_t<decltype(str1())>;
    static_assert(std::is_same_v<char_type, const_c_str_char_t<decltype(str2())>>);

    return std::integer_sequence<char_type, str1()[indexes1]..., str2()[indexes2]...>{};
}

template <class T, T ...values1, T ...values2>
constexpr decltype(auto) append_sequence(std::integer_sequence<T, values1...>, std::integer_sequence<T, values2...>) {
    return std::integer_sequence<T, values1..., values2...>{};
}


template <class sequence_t>
struct string_sequence_to_view;

template <class char_type, char_type ...chars>
struct string_sequence_to_view<std::integer_sequence<char_type, chars...>>
{
    using string_view_t = std::conditional_t<std::is_same_v<char_type, char>, std::string_view, std::wstring_view>;

    static constexpr decltype(auto) get() {
        return string_view_t{c_str};
    }

    static constexpr const char_type c_str[]{chars..., char_type{}};
};

template <class char_type, size_t value, std::enable_if_t<std::is_same_v<char_type, char> || std::is_same_v<char_type, wchar_t>, int> = 0>
constexpr decltype(auto) integer_to_string_sequence()
{
    constexpr auto digits = []()
    {
        if constexpr (std::is_same_v<char_type, char>) {
            return "0123456789abcdefghijklmnopqrstuvwxyz";
        }
        else if constexpr (std::is_same_v<char_type, wchar_t>) {
            return L"0123456789abcdefghijklmnopqrstuvwxyz";
        }
    };

    constexpr size_t remainder = value % 10;
    constexpr size_t next_value = value / 10;

    if constexpr (next_value != 0) {
        return append_sequence(integer_to_string_sequence<char_type, next_value>(), std::integer_sequence<char_type, digits()[remainder]>{});
    }
    else {
        return std::integer_sequence<char_type, digits()[remainder]>{};
    }
}

#define INT_TO_C_STR(char_type, num)    string_sequence_to_view<decltype(integer_to_string_sequence<char_type, num>())>{}.c_str

#define APPEND_C_STR_AS_VIEW(s1, s2)                                            \
    string_sequence_to_view<                                                    \
        decltype(                                                               \
            string_append_sequence<                                             \
                [] { return s1; },                                              \
                [] { return s2; }                                               \
            >(                                                                  \
                std::make_index_sequence<sizeof(s1) / sizeof(s1[0]) - 1>(),     \
                std::make_index_sequence<sizeof(s2) / sizeof(s1[0]) - 1>()      \
            )                                                                   \
        )                                                                       \
    >{}.get()

// Number of strings that we want to generate
constexpr size_t NumberOfTextsToGenerate = 20u;

// constexpr function to build a string
template <size_t i>
constexpr std::string_view makeString() {
    return APPEND_C_STR_AS_VIEW("test", INT_TO_C_STR(char, i));
}

template <size_t i>
constexpr std::wstring_view makeStringW() {
    return APPEND_C_STR_AS_VIEW(L"test", INT_TO_C_STR(wchar_t, i));
}

// Helper: constexpr function that will create an array of string_views and initialize it
template <size_t... ManyIntegers>
constexpr auto generateTextHelper(std::integer_sequence<size_t, ManyIntegers...>) {
    return std::array<std::string_view, sizeof...(ManyIntegers)>{ makeString<ManyIntegers>()...};
}

template <size_t... ManyIntegers>
constexpr auto generateTextHelperW(std::integer_sequence<size_t, ManyIntegers...>) {
    return std::array<std::wstring_view, sizeof...(ManyIntegers)>{ makeStringW<ManyIntegers>()...};
}

// Helper: constexpr function that will return an array of string_views as shown above with a specified number of texts
constexpr auto generateTextArray() {
    return generateTextHelper(std::make_integer_sequence<size_t, NumberOfTextsToGenerate>());
}

constexpr auto generateTextArrayW() {
    return generateTextHelperW(std::make_integer_sequence<size_t, NumberOfTextsToGenerate>());
}

// This is a definition of a std::array<std::string_view,UpperBound> initialized with some text
constexpr auto text = generateTextArray();
constexpr auto textW = generateTextArrayW();

int main()
{
    for (size_t i{}; i < NumberOfTextsToGenerate; ++i) {
        std::cout << text[i] << '\n';
    }

    for (size_t i{}; i < NumberOfTextsToGenerate; ++i) {
        std::wcout << textW[i] << L'\n';
    }

    return 0;
}

Output: Output:

test0
test1
test2
test3
test4
test5
test6
test7
test8
test9
test10
test11
test12
test13
test14
test15
test16
test17
test18
test19

EDIT: Supported wchar_t string.编辑:支持的wchar_t字符串。

I now found a solution that finally satisfies my needs.我现在找到了最终满足我需求的解决方案。

Because of many good answers by SO users and additional answers to my question here ,由于 SO 用户提供了许多很好的答案以及我的问题其他答案,

I will now use the following code.我现在将使用以下代码。

#include <iostream>
#include <algorithm>
#include <iterator>
#include <array>
#include <string>

// Specification for the output that we want to generate
constexpr const char BaseString[]{ "text" };    // Some example text
constexpr size_t StartIndex = 1u;               // Start index. So first array element will be "Text1"
constexpr size_t NumberOfElements = 20u;        // Number of elements to create. Last array element will be "Text20"

// These templates are used to generate a std::array
namespace ConstexprGenerator {
    // To create something like "text123" as constexpr
    template <const size_t numberToConvert, const char* Text>
    class Converter {
    public:
        // Some helper variables for calculating sizes
        static constexpr size_t TextLength{ std::char_traits<char>::length(Text) };
        static constexpr size_t NumberOfDigits{ ([]() constexpr noexcept {size_t result = 0; int temp = numberToConvert; for (; temp != 0; temp /= 10) ++result; return result; }()) };
        static constexpr size_t ArrayLength{ (numberToConvert ? 1u : 2u) + NumberOfDigits + TextLength };

        // Here we will store the string
        std::array<char, ArrayLength> internalString{};

        // Constructor: Copy text and Convert number to character digits
        constexpr Converter() noexcept {
            size_t i{ 0 };  for (; i < TextLength; ++i) internalString[i] = Text[i]; // Copy text
            if (numberToConvert == 0) internalString[i] = '0';  // In case that the given number is 0, then simply copy '0' character   
            else {
                i = NumberOfDigits + TextLength - 1;            // Convert number to character digits
                int number = numberToConvert; for (; number; number /= 10)
                    internalString[i--] = number % 10 + '0';
            }
        }
        constexpr std::array<char, ArrayLength> get() const { return *this; };              // getter
        constexpr operator std::array<char, ArrayLength>() const { return internalString; } // type cast
    };

    // Templated variable. Will have always a different type, depending on the template parameters
    template<const size_t numberToConvert, const char* Text>
    constexpr auto Converted = Converter<numberToConvert, Text>{}.get();

    // Generate a std::array with n elements that consist of const char *, pointing to Textx...Texty
    template <int... ManyIntegers>
    constexpr auto generateTextHelper(std::integer_sequence<size_t, ManyIntegers...>) noexcept {
        return std::array<const char*, sizeof...(ManyIntegers)>{ {Converted<ManyIntegers + StartIndex, BaseString>.data()...}};
    }
    // Generate the required number of texts
    constexpr auto generateTextArray()noexcept {
        return generateTextHelper(std::make_integer_sequence<size_t, NumberOfElements>());
    }
}
// This is a constexpr array
constexpr auto text = ConstexprGenerator::generateTextArray();

int main() {
    std::copy(text.begin(), text.end(), std::ostream_iterator<const char*>(std::cout, "\n"));
    return 0;
}

Tested with MSVC, Clang and gcc使用 MSVC、Clang 和 gcc 测试

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM