捕获函数参数中的constexpr-ness

时间:2015-10-07 20:57:08

标签: c++ c++14 constexpr

由于各种原因,我正在寻找一种方法来捕获传递给函数的参数的constexpr-ness。解释起来有点棘手,所以我认为代码最能说明我想要实现的目标

#include <vector> // For std::size_t
#include <cstdio>

namespace
{
  template<std::size_t N, typename ...TArgs>
  constexpr int cstrlen (char const (&s) [N], std::size_t i = 0) noexcept
  {
    return i < N && s[i] != 0
      ? 1 + cstrlen (s, i + 1)
      : 0
      ;
  }

  template<std::size_t N, typename ...TArgs>
  inline void silly_printf (char const (&format) [N], TArgs && ...args) noexcept
  {
    static_assert (cstrlen (format) > 0, "format must not be empty string");
    printf (format, std::forward<TArgs> (args)...);
  }

}

#define SILLY_PRINTF(format, ...)                                           \
  static_assert (cstrlen (format) > 0, "format must not be empty string");  \
  printf (format, ##__VA_ARGS__);

int main()
{
  // This works but relies on macros
  SILLY_PRINTF ("Hello: %d", 1);

  // This doesn't work
  silly_printf ("Hello: %d", 1);
  return 0;
}

我无法让silly_printf按照我的意愿去工作。编译器抱怨表达式不会计算为常量。我们知道在使用字符串文字调用silly_print但是constexpr-ness丢失时这是constexpr(我在这里使用的是VS2015)。

我想也许我可以将constexpr添加到参数中(很像const)但是没有成功。

我可以使用宏来解决这个问题(由SILLY_PRINTF宏演示),但感觉就像失败一样。

欢迎任何想法。

PS。我真正想要实现的目标略显愚蠢

2 个答案:

答案 0 :(得分:1)

您不需要使用char数组引用作为参数。这是我使用的一个,但你需要有c ++ 14宽松的constexpr规则:

using size_t=decltype(sizeof(int));

namespace util
{
    template<typename char_t>
    constexpr size_t str_size(const char_t*)noexcept;
}

template
<typename char_t>
constexpr auto
util::
str_size
(const char_t* const a_str)noexcept->size_t
{
    const char_t* a_char=a_str;

    while(*a_char!=char_t(0))
    {
        ++a_char;
    }

    return size_t(a_char-a_str);
}

static_assert(util::str_size("hello")==size_t(5),"");

如果你不能使用c ++ 14,递归版本也会起作用。您仍然只使用char指针作为参数而不是char数组引用。

答案 1 :(得分:1)

有一个GNU扩展(由g ++和clang支持)允许用户定义的表单文字:

template<typename CharT, CharT... Chars>
constexpr void operator"" _something() { }

有了这个,可以构建一个没有宏的constexpr-string类型,可以像这样使用:

constexpr auto str = "testing\0length"_string;
static_assert(str.strlen() == 7, "!");

通过将所有字符串的属性编码到类型中,然后可以在任何地方static_assert,constexpr或不是。 例如,在你的silly_printf:

template<typename CharT, CharT... Chars, typename... Args>
void silly_printf(const constexpr_string<CharT, Chars...>& format_string, Args&&... args) {
    static_assert(format_string.strlen() > 0, "format string must not be empty");
    printf(format_string.c_str(), args...);
}

并像这样使用它:

silly_printf("testing %d %s %x embedded\0null"_string, 1, "2", nullptr);

您还可以使用另一个operator"" _silly_printf()返回一个函数对象来获取"format string"_silly_printf(args...)之类的语法。

See it live on Coliru