简体   繁体   English

C ++如何改进这个模板元程序,以回馈包含大小的数组?

[英]C++ How can I improve this bit of template meta-program to give back the array including the size?

I've got a utility called choose_literal which chooses a literal string encoded as char*, wchar_*, char8_t*, char16_t*, char32_t* depending on the desired type (the choice). 我有一个名为choose_literal的实用程序,它根据所需的类型(选择)选择编码为char*, wchar_*, char8_t*, char16_t*, char32_t*的文字字符串。

It looks like this: 它看起来像这样:

    template <typename T>
    constexpr auto choose_literal(const char * psz, const wchar_t * wsz, const CHAR8_T * u8z, const char16_t * u16z, const char32_t * u32z) {
        if constexpr (std::is_same_v<T, char>)
            return psz;
        if constexpr (std::is_same_v<T, wchar_t>)
            return wsz;
    #ifdef char8_t
        if constexpr (std::is_same_v<T, char8_t>)
            return u8z;
    #endif
        if constexpr (std::is_same_v<T, char16_t>)
            return u16z;
        if constexpr (std::is_same_v<T, char32_t>)
            return u32z;
    }

I supply a little preprocessor macro to make this work w/o having to type each of those string encodings manually: 我提供了一个小预处理器宏来完成这项工作,而不必手动输入每个字符串编码:

// generates the appropriate character literal using preprocessor voodoo
// usage: LITERAL(char-type, "literal text")
#define LITERAL(T,x) details::choose_literal<T>(x, L##x, u8##x, u##x, U##x)

This of course only works for literal strings which can be encoded in the target format by the compiler - but something like an empty string can be, as can ASCII characters (ie az, 0-9, etc., which have representations in all of those encodings). 这当然只适用于可由编译器以目标格式编码的文字字符串 - 但类似于空字符串的东西可以是ASCII字符(即az,0-9等,它们在所有字符串中都有表示形式)那些编码)。

eg here's a trivial bit of code that will return the correct empty-string given a valid character type 'T': 例如,这里有一些简单的代码,如果给出有效的字符类型'T',它将返回正确的空字符串:

template <typename T>
constexpr const T * GetBlank() {
    return LITERAL(T, "");
}

This is great as far as it goes, and it works well enough in my code. 这是很好的,它在我的代码中运行良好。

What I'd like to do is to refactor this such that I get back the character-array including its size, as if I'd written something like: 我想做的是重构这个,以便我回到包括它的大小的字符数组,好像我写了类似的东西:

const char blank[] = "";

or 要么

const wchar_t blank[] = L"";

Which allows the compiler to know the length of the string-literal, not just its address. 这允许编译器知道字符串文字的长度,而不仅仅是它的地址。

My choose_literal<T>(str) returns only the const T * rather than the const T (&)[size] which would be ideal. 我的choose_literal<T>(str)只返回const T *而不是const T (&)[size] ,这将是理想的。

In general I'd love to be able to pass such entities around intact - rather than have them devolve into just a pointer. 一般来说,我希望能够完整地传递这些实体 - 而不是让它们只是指向一个指针。

But in this specific case, is there a technique you might point me towards that allows me to declare a struct with a data-member for the desired encoding which then also knows its array-length? 但是在这种特定的情况下,是否有一种技术可以指向我,允许我声明一个带有数据成员的结构,用于所需的编码,然后它也知道它的数组长度?

A little bit of constexpr recursion magic allows you to return a string_view of the appropriate type. 一点constexpr递归魔术允许您返回相应类型的string_view

#include <string_view>
#include <type_traits>
#include <iostream>

template <typename T, class Choice, std::size_t N, class...Rest>
constexpr auto choose_literal(Choice(& choice)[N], Rest&...rest)
{
    using const_char_type = Choice;

    using char_type = std::remove_const_t<const_char_type>;

    if constexpr (std::is_same_v<T, char_type>)
    {
        constexpr auto extent = N;
        return std::basic_string_view<char_type>(choice, extent - 1);
    }
    else
    {
        return choose_literal<T>(rest...);
    }
}

int main()
{
    auto clit = choose_literal<char>("hello", L"hello");
    std::cout << clit;

    auto wclit = choose_literal<wchar_t>("hello", L"hello");
    std::wcout << wclit;
}

https://godbolt.org/z/4roZ_O https://godbolt.org/z/4roZ_O

If it were me, I'd probably want to wrap this and other functions into a constexpr class which offers common services like printing the literal in the correct form depending on the stream type, and creating the correct kind of string from the literal. 如果是我,我可能想把这个和其他函数包装成一个constexpr类,它提供常见的服务,比如根据流类型以正确的形式打印文字,并从文字中创建正确的字符串。

For example: 例如:

#include <string_view>
#include <type_traits>
#include <iostream>
#include <tuple>

template <typename T, class Choice, std::size_t N, class...Rest>
constexpr auto choose_literal(Choice(& choice)[N], Rest&...rest)
{
    using const_char_type = Choice;

    using char_type = std::remove_const_t<const_char_type>;

    if constexpr (std::is_same_v<T, char_type>)
    {
        constexpr auto extent = N;
        return std::basic_string_view<char_type>(choice, extent - 1);
    }
    else
    {
        return choose_literal<T>(rest...);
    }
}

template<class...Choices>
struct literal_chooser
{
    constexpr literal_chooser(Choices&...choices)
    : choices_(choices...)
    {}

    template<class T>
    constexpr auto choose() 
    {
        auto invoker = [](auto&...choices)
        {
            return choose_literal<T>(choices...);
        }; 

        return std::apply(invoker, choices_);
    }

    std::tuple<Choices&...> choices_;
};

template<class Char, class...Choices>
std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& os, literal_chooser<Choices...> chooser)
{
    return os << chooser.template choose<Char>();
}

template<class Char, class...Choices>
std::basic_string<Char> to_string(literal_chooser<Choices...> chooser)
{
    auto sview = chooser.template choose<Char>();
    return std::basic_string<Char>(sview.data(), sview.size());
}


int main()
{
    auto lit = literal_chooser("hello", L"hello");

    std::cout << lit << std::endl;
    std::wcout << lit << std::endl;

    auto s1 = to_string<char>(lit);
    auto s2 = to_string<wchar_t>(lit);

    std::cout << s1 << std::endl;
    std::wcout << s2 << std::endl;   
}

The use of the reference argument type Choices& is important. 使用引用参数类型Choices&很重要。 C++ string literals are references to arrays of const Char . C ++字符串文字是对const Char数组的引用。 Passing by value would result in the literal being decayed into a pointer, which would lose information about the extent of the array. 按值传递将导致文字被衰减为指针,这将丢失有关数组范围的信息。

we can add other services, written in terms of the literal_chooser: 我们可以添加其他服务,用literal_chooser编写:

template<class Char, class...Choices>
constexpr std::size_t size(literal_chooser<Choices...> chooser)
{
    auto sview = chooser.template choose<Char>();
    return sview.size();
}

We're going to change the function so that it takes a const T (&)[size] for each input, and the return type is going to be decltype(auto) . 我们将更改函数,以便为每个输入采用const T (&)[size] ,返回类型将为decltype(auto) Using decltype(auto) prevents the return from decaying into a value, preserving things like references to arrays. 使用decltype(auto)可以防止返回值衰减为值,从而保留了对数组的引用等内容。

Updated function: 更新功能:

template <typename T, size_t N1, size_t N2, size_t N3, size_t N4>
constexpr decltype(auto) choose_literal(const char (&psz)[N1], const wchar_t (&wsz)[N2], const char16_t (&u16z)[N3], const char32_t (&u32z)[N4]) {
    if constexpr (std::is_same<T, char>())
        return psz;
    if constexpr (std::is_same<T, wchar_t>())
        return wsz;
    if constexpr (std::is_same<T, char16_t>())
        return u16z;
    if constexpr (std::is_same<T, char32_t>())
        return u32z;
}

In main, we can assign the result to something of type auto&& : 在main中,我们可以将结果分配给auto&&类型的东西:

#define LITERAL(T,x) choose_literal<T>(x, L##x,  u##x, U##x)

int main() {
    constexpr auto&& literal = LITERAL(char, "hello");  
    return sizeof(literal); // Returns 6
}

Potential simplification 潜在的简化

We can simplify the choose_literal function by making it recursive, that way it can be expanded for any number of types. 我们可以通过使其递归来简化choose_literal函数,这样就可以针对任意数量的类型进行扩展。 This works without any changes to the LITERAL macro. 这不会对LITERAL宏进行任何更改。

template<class T, class Char, size_t N, class... Rest>
constexpr decltype(auto) choose_literal(const Char(&result)[N], Rest const&... rest) {
    if constexpr(std::is_same_v<T, Char>)
        return result; 
    else
        return choose_literal<T>(rest...);
}

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

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