基于模板编译时间字符串生成器

Compile time string generator based on templates

我正在开发基于mfc 的GUI。其中一个控件允许标记语言 (XAML) 在其中呈现文本和图标,因此我正在大量使用它。所有控件的标记字符串几乎相同(一个表单中有 12 个),所以我想知道这是否是一种生成该字符串的函数的简单方法。

所以控件是预定义的,在执行过程中不会改变,我想在编译时生成这些字符串,就像直接输入然后编译一样。

我正在使用 boost::hana,但我无法通过我尝试过的任何方式在模板中使用字符串文字。我知道由于编译时内存分配未知,无法在模板中直接使用字符串文字。

除了在结构中使用 externstatic 分配的经典技巧外,由于增加了语法复杂性,我不喜欢 C++17(或更低版本,但请不要使用 c++20) 来允许这个?

注意 - 一个额外的应该是在不使用宏的函数中直接格式化 int(使用 10 而不是“10”)。

接下来是代码,当然,它不会以当前形式编译:

#include <iostream>
#include <boost/hana/string.hpp>
namespace hana = boost::hana;

constexpr auto markup1()
{
    return (BOOST_HANA_STRING(R"(<Border Padding='4' BorderThickness='2' BorderBrush='#767676' Background='#e4ecf7'>
            <StackPanel TextBlock.FontFamily='Tahoma'>
            <Border Margin='4' Padding='4' Background='#FF212C'><TextBlock HorizontalAlignment='Center' Foreground='#ffffff' FontSize='24' FontWeight='Bold'>)") +

        BOOST_HANA_STRING("USERS") +

        BOOST_HANA_STRING(R"(</TextBlock></Border>
            <TextBlock Padding='9, 6, 30, 7' FontWeight='Bold' TextWrapping='Wrap'>)") +

        BOOST_HANA_STRING("User Management. Allows creation of administrators and users.") +

        BOOST_HANA_STRING(R"(</TextBlock>
            <Border Height='1' Background='#9ebbdd'/>
            <Border Height='1' Background='White'/>
            <Image Source=')") +

        BOOST_HANA_STRING("10") + 

        BOOST_HANA_STRING(R"(' Margin='10' VerticalAlignment='Center'/>
            </StackPanel>
            </Border>)") ).c_str();
}

template <const char* header, const char* description, const char* image>
constexpr auto markup2()
{
    return (BOOST_HANA_STRING(R"(<Border Padding='4' BorderThickness='2' BorderBrush='#767676' Background='#e4ecf7'>
            <StackPanel TextBlock.FontFamily='Tahoma'>
            <Border Margin='4' Padding='4' Background='#FF212C'><TextBlock HorizontalAlignment='Center' Foreground='#ffffff' FontSize='24' FontWeight='Bold'>)") +

        BOOST_HANA_STRING(header) +

        BOOST_HANA_STRING(R"(</TextBlock></Border>
            <TextBlock Padding='9, 6, 30, 7' FontWeight='Bold' TextWrapping='Wrap'>)") +

        BOOST_HANA_STRING(description) +

        BOOST_HANA_STRING(R"(</TextBlock>
            <Border Height='1' Background='#9ebbdd'/>
            <Border Height='1' Background='White'/>
            <Image Source=')") +

        BOOST_HANA_STRING(image) + 

        BOOST_HANA_STRING(R"(' Margin='10' VerticalAlignment='Center'/>
            </StackPanel>
            </Border>)") ).c_str();
}

int main()
{
    std::string str1 { markup1() };
    std::string str2 { markup2<"USERS", "User Management. Allows creation of administrators and users.", "10">() };
    
    std::cout << std::boolalpha << (str1 == str2); 
}

可直接访问 coliru:http://coliru.stacked-crooked.com/a/4a5bc326be33cb3e

将字符串文字直接作为模板参数传递是不合法的;但是,可以将字符串文字绑定到 constexpr 数组并改为(通过引用)传递:

template<auto const& header, auto const& description, auto const& image>
constexpr auto markup2() { ... }

int main() {
    static constexpr char const h2[] = "USERS";
    static constexpr char const d2[] = "User Management. Allows creation of administrators and users.";
    static constexpr char const i2[] = "10";

    std::string_view const str1{ markup1() },
                           str2{ markup2<h2, d2, i2>() };
    return str1 == str2; 
}

Online Demo

当然,在预先编写了一些样板之后,C++20 有多个选项可以使它更漂亮。

您可以将 HANA_STRING 作为“常规”(需要是模板)参数:

template <typename Header, typename Description, typename Image>
constexpr auto markup2(Header header, Description description, Image image)
{
    return (BOOST_HANA_STRING(
R"(<Border Padding='4' BorderThickness='2' BorderBrush='#767676' Background='#e4ecf7'>
<StackPanel TextBlock.FontFamily='Tahoma'>
<Border Margin='4' Padding='4' Background='#FF212C'>
<TextBlock HorizontalAlignment='Center' Foreground='#ffffff' FontSize='24' FontWeight='Bold'>)")

    + header
    + BOOST_HANA_STRING(
R"(</TextBlock></Border>
<TextBlock Padding='9, 6, 30, 7' FontWeight='Bold' TextWrapping='Wrap'>)")

    + description
    + BOOST_HANA_STRING(
R"(</TextBlock>
<Border Height='1' Background='#9ebbdd'/>
<Border Height='1' Background='White'/>
<Image Source=')")

    + image

    + BOOST_HANA_STRING(
R"(' Margin='10' VerticalAlignment='Center'/>
</StackPanel>
</Border>)") ).c_str();
}

调用类似于

std::string str2 { markup2(
    BOOST_HANA_STRING("USERS"),
    BOOST_HANA_STRING("User Management. Allows creation of administrators and users."),
    BOOST_HANA_STRING("10")) };

Demo