Search code examples
c++c++17boost-hana

Compile time string generator based on templates


I am developing a GUI based on mfc. One of the controls allows a markup languge (XAML) to render text and icons inside it, so I am doing an intensive use of it. The markup string is almost the same for all the controls (12 in a form), so I wondered if it would be an easy way to make a function to generate that string.

So the control are predefined and does not change during execution, I would like to generate those strings at compile time, as well as if they were directly typed and then compiled.

I am using boost::hana for it, but I am not able of using string literals in a template by any of the ways I have tried. I know that there is not posible to directly use string literals in templates due to unknown memory allocation at compile time.

Besides the classical tricks of using extern or static allocation in a struct, what I dislike due to adding syntax complexity, is there any way in C++17 (or below, but NOT c++20 please) to allow this?

Note- An extra should be to directly format the int (use 10 instead of "10") in the function not using a macro.

The code is next, and of course, it does NOT compile in its current form:

#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); 
}

A direct access to coliru is available: http://coliru.stacked-crooked.com/a/4a5bc326be33cb3e


Solution

  • It's not legal to pass string literals directly as template arguments, ever; however, one may bind the string literal to a constexpr array and pass that (by reference) instead:

    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

    Naturally, C++20 has multiple options for making this a bit prettier, after writing some boilerplate up front.