根据一些字符串文字和一个int模板参数的连接创建一个constexpr C字符串

时间:2018-11-07 12:39:46

标签: c++ string constexpr

我有一个带有int模板参数的类。在某些情况下,我希望它输出错误消息。此消息应该是来自一些固定文本和模板参数的连接字符串。出于性能方面的考虑,我希望避免在每次错误发生时都在运行时建立此字符串,并且从理论上讲,在编译时都知道字符串文字和template参数。因此,我正在寻找将其声明为constexpr的可能性。

代码示例:

template<int size>
class MyClass
{
    void onError()
    {
        // obviously won't work but expressing the concatenation like
        // it would be done with a std::string for clarification 
        constexpr char errMsg[] = "Error in MyClass of size " + std::to_string (size) + ": Detailed error description\n";

        outputErrorMessage (errMsg);
    }
}

3 个答案:

答案 0 :(得分:5)

使用class Parent: __props__ = (('a', str, 'a var'), ('b', int, 'b var')) def __init_subclass__(cls, __props__=(), **kwargs): super().__init_subclass__(**kwargs) cls.__props__ = cls.__props__ + __props__ class Child(Parent, __props__=(('c', str, 'foo'),)): pass print(Child.__props__) # (('a', <class 'str'>, 'a var'), ('b', <class 'int'>, 'b var'), ('c', <class 'str'>, 'foo')) class GrandChild(Child, __props__=(('d', float, 'd var'),)): pass print(GrandChild.__props__) # (('a', <class 'str'>, 'a var'), ('b', <class 'int'>, 'b var'), # ('c', <class 'str'>, 'foo'), ('d', <class 'float'>, 'd var')) 只能计算一次(但在运行时):

static const

如果您真的想在编译时使用该字符串,则可以使用template<int size> class MyClass { void onError() { static const std::string = "Error in MyClass of size " + std::to_string(size) + ": Detailed error description\n"; outputErrorMessage(errMsg); } }; ,类似于:

std::array

最后:

template <std::size_t N>
constexpr std::size_t count_digit() {
    if (N == 0) {
        return 1;   
    }
    std::size_t res = 0;
    for (int i = N; i; i /= 10) {
        ++res;
    }
    return res;
}

template <std::size_t N>
constexpr auto to_char_array()
{
    constexpr auto digit_count = count_digit<N>();
    std::array<char, digit_count> res{};

    auto n = N;
    for (std::size_t i = 0; i != digit_count; ++i) {
        res[digit_count - 1 - i] = static_cast<char>('0' + n % 10);
        n /= 10;
    }
    return res;
}

template <std::size_t N>
constexpr std::array<char, N - 1> to_array(const char (&a)[N])
{
    std::array<char, N - 1> res{};

    for (std::size_t i = 0; i != N - 1; ++i) {
        res[i] = a[i];
    }
    return res;
}

template <std::size_t ...Ns>
constexpr std::array<char, (Ns + ...)> concat(const std::array<char, Ns>&... as)
{
    std::array<char, (Ns + ...)> res{};
    std::size_t i = 0;
    auto l = [&](const auto& a) { for (auto c : a) {res[i++] = c;} };

    (l(as), ...);
    return res;
}

Demo

答案 1 :(得分:2)

这是我的解决方案。经过Godbolt测试:

#include <string_view>
#include <array>
#include <algorithm>

void outputErrorMessage(std::string_view s);

template<int N> struct cint
{
    constexpr int value() const { return N; }
};

struct concat_op {};

template<std::size_t N>
struct fixed_string
{
    constexpr static std::size_t length() { return N; }
    constexpr static std::size_t capacity() { return N + 1; }

    template<std::size_t L, std::size_t R>
    constexpr fixed_string(concat_op, fixed_string<L> l, fixed_string<R> r)
    : fixed_string()
    {
        static_assert(L + R == N);   
        overwrite(0, l.data(), L);
        overwrite(L, r.data(), R);     
    }

    constexpr fixed_string()
    : buffer_ { 0 }
    {
    }

    constexpr fixed_string(const char (&source)[N + 1])
    : fixed_string()
    {
        do_copy(source, buffer_.data());
    }

    static constexpr void do_copy(const char (&source)[N + 1], char* dest)
    {
        for(std::size_t i = 0 ; i < capacity() ; ++i)
            dest[i] = source[i];
    }

    constexpr const char* data() const
    {
        return buffer_.data();
    }

    constexpr const char* data()
    {
        return buffer_.data();
    }

    constexpr void overwrite(std::size_t where, const char* source, std::size_t len)
    {
        auto dest = buffer_.data() + where;
        while(len--)
            *dest++ = *source++;
    }

    operator std::string_view() const
    {
        return { buffer_.data(), N };
    }

    std::array<char, capacity()> buffer_;
};

template<std::size_t N> fixed_string(const char (&)[N]) -> fixed_string<N - 1>;

template<std::size_t L, std::size_t R>
constexpr auto operator+(fixed_string<L> l, fixed_string<R> r) -> fixed_string<L + R>
{
    auto result = fixed_string<L + R>(concat_op(), l , r);
    return result;
};

template<int N> 
constexpr auto to_string()
{
    auto log10 = []
    {
        if constexpr (N < 10)
            return 1;
        else if constexpr(N < 100)
            return 2;
        else if constexpr(N < 1000)
            return 3;
        else 
            return 4; 
            // etc
    };

    constexpr auto len = log10();

    auto result = fixed_string<len>();

    auto pow10 = [](int n, int x)
    {
        if (x == 0)
            return 1;
        else while(x--)
            n *= 10;
        return n;
    };

    auto to_char = [](int n)
    {
        return '0' + char(n);
    };

    int n = N;
    for (int i = 0 ; i < len ; ++i)
    {
        auto pow = pow10(10, i);
        auto digit = to_char(n % 10);
        if (n == 0 && i != 0) digit = ' ';
        result.buffer_[len - i - 1] = digit;
        n /= 10;
    }

    return result;
}

template<int size>
struct MyClass
{
    void onError()
    {
        // obviously won't work but expressing the concatenation like
        // it would be done with a std::string for clarification 
        static const auto errMsg = fixed_string("Error in MyClass of size ") + to_string<size>() + fixed_string(": Detailed error description\n");


        outputErrorMessage (errMsg);
    }
};

int main()
{
    auto x = MyClass<10>();
    x.onError();
}

结果如下:

main:
  sub rsp, 8
  mov edi, 56
  mov esi, OFFSET FLAT:MyClass<10>::onError()::errMsg
  call outputErrorMessage(std::basic_string_view<char, std::char_traits<char> >)
  xor eax, eax
  add rsp, 8
  ret

https://godbolt.org/z/LTgn4F

更新:

不需要调用pow10。这是可以删除的无效代码。

答案 2 :(得分:0)

很遗憾,您的选择是有限的。 C ++不允许将字符串文字用作模板参数,即使这样做,文字串联也要在模板进入模板之前在预处理器中进行。您将需要一些令人讨厌的逐字符数组定义和一些手动的int到char转换。太可怕了以至于我无法让自己去尝试,而太可怕了以至于老实说我建议不要打扰。我会在运行时生成它,尽管只有一次(您可以使errMsg一个函数-至少static std::string)。