属性的std::span可能是静态的或动态的前提条件。

huangapple go评论70阅读模式
英文:

preconditions for properties of a std::span that could be static or dynamic

问题

I have a function that is generic over the extent of a std::span it receives as a parameter. I want to do some checks on it as efficiently as possible while avoiding duplication but guaranteeing a compile error when it's possible.

My goal is to:

  1. Error as early as possible, always at compile time when possible.
  2. Generate a good error message at compile time (static assert failure with file and line number), and at runtime in debug builds (assertion error with file and line number), but have the minimal required overhead in release builds.
  3. Have zero runtime overhead if checked at compile time. If the runtime check is included it should have an optimization hint in release configuration.

For example, I have the following function that does preflight checks on a std::span before passing its raw contents to a fast implementation that does no checks. Since it's templated on span's Extent parameter I can sometimes do these checks at compile time. The problem is that leads me to repeat the precondition pairs.size() > 0 && pairs.size() % 2 == 0 three times, depending on whether I am using static_assert, assert, or a cheap release mode runtime check:

#include <span>
#ifndef NDEBUG
#include <cassert>
#endif

extern long SumOfDistancesFast(const long *, size_t) noexcept;

template<size_t Extent = std::dynamic_extent>
[[nodiscard]]
constexpr long SumOfDistances(std::span<const long, Extent> pairs) noexcept
{
    static_assert(Extent == decltype(pairs)::extent);
    if constexpr (Extent != std::dynamic_extent) {
        static_assert(pairs.size() == Extent);
        static_assert(pairs.size() > 0 && pairs.size() % 2 == 0);
    } else {
#ifdef NDEBUG
        if (!(pairs.size() > 0 && pairs.size() % 2 == 0)) {
            [[unlikely]] __builtin_trap();
        }
#else
        assert(pairs.size() > 0 && pairs.size() % 2 == 0);
#endif
    }

    return SumOfDistancesFast(pairs.data(), pairs.size());
}

([Godbolt](https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIApACYAQuYukl9ZATwDKjdAGFUtAK4sGIAGzSrgAyeAyYAHI%2BAEaYxCBmABykAA6oCoRODB7evgHSaRmOAqHhUSyx8Um2mPbFDEIETMQEOT5%2BgTV1WY3NBKWRMXEJyQpNLW15nWN9A%2BWVIwCUtqhexMjsHOYAzGHI3lgA1CbbbgophifYJhoAgjt4VAxYVIcRACLYFnIA4jf3Zl2DH2XiOJzcogUShaV3%2BO1cjzhd0wqgIcQYh1oAmAhyEPgA8lR3ngZsDMAoAGJMMYQNAMMaY7GHABUpEOGQAXpgAPoERaHBioFEbFIEE5WO7/NEsFIGNHgzk8giHbCoxjKk7vdkEdAgEDoACezBYeGQ3JRaMEsMlAFYrHbBfgFKJiOgTDb3u7PXc6WMUSliIyjLiCUSSU0yQoIGNdSBzpdTr7lVijGzVZaxdtsIcLnhiAp%2BYLhZhRXCAOwS26HauHAD0tcOACUSwYNocCAgSSB/jXtUxHGbqdCCBB0%2BrjttPZPDlh9gQDSlMBBc/nFnqLerFuKezXHock/7A6O1YJDmAwJrtbHDcbTeaT3zjhWd73qzMB9yh3ERyuFAA6RUIH5TVLzHQQt22StXxrd87y/FplyYPN/0A4Cs0ODRjjMfxzH8HMkPzAC8C5ICsJtQ4zAnKctQ0CCoJrEwyy1WolCfSsHioF43k%2Bb4/juaC9wgc9EOQoiSLQ7NMNw3D8NE1CyIoqjL1o4C7XdCwvAYWg8AAa1qA0vS9Q5uW5aIvDoRwGF5YgmBSIDt0lQEWMwF8a3gn8CJQ4ilwkjCsJw7DZMI%2BTzHIyiQOnWiHIBbYESoVzGO9e5%2BJretDgAdUIBB207BRuxS6s0oDTA6XwepjwzJTp1nWh50XETV3XB86NcoriBKgQyqyCrxwirUYz1G9WDvDdT0YtxxqCrzxKqrUwL5aLezajrnkyAQGumnyJ0k/yZN/MSttCxS%2BowlrJQKw52oINYMTxFhCWJUkNkpakPNE9B%2ByYIC2X21CzvuJikXuNS7X2QxQBAVBRUFcJDI9OsG2KgA3ccwm0hgwmAf4UxxO6HvDQxnqpGkkyDHFmV/NlFV5KbuUVQshVUEUNWfC60oAFXxd58RAJsWyYNsAHcssOL8KloA1DhR/MslcnHLvJLxaqojDFprfgj2p5U8BVjRxUOHXwVpxU6wo/XDcsS8zGA1mq2ghWFCVjVLenX91LwIyAFopvdrCLEOLhDMghLAYuq6bodp3osSoGHmBA5MAnNxmhsgyszhQE9gTpO6WZhRrRirPQUT8EnDGdrWALuOQTBU4UYcEgq7uMJlRYJCGFIxj6LfHU9XrohiHBHGrkj2r8%2BD867cKhs8WQDZMHQBQ2WQBASr09BReVNAZToRPHDYVzSflt2Qa1S8

英文:

I have a function that is generic over the extent of a std::span it receives as a parameter. I want to do some checks on it as efficiently as possible while avoiding duplication but guaranteeing a compile error when it's possible.

My goal is to:

  1. Error as early as possible, always at compile time when possible.
  2. Generate a good error message at compile time (static assert failure with file and line number), and at runtime in debug builds (assertion error with file and line number), but have the minimal required overhead in release builds.
  3. Have zero runtime overhead if checked at compile time. If the runtime check is included it should have an optimization hint in release configuration.

For example, I have the following function that does preflight checks on a std::span before passing its raw contents to a fast implementation that does no checks. Since it's templated on span's Extent parameter I can sometimes do these checks at compile time. The problem is that leads me to repeat the precondition pairs.size() &gt; 0 &amp;&amp; pairs.size() % 2 == 0 three times, depending on whether I am using static_assert, assert, or a cheap release mode runtime check:

#include &lt;span&gt;
#ifndef NDEBUG
#include &lt;cassert&gt;
#endif

extern long SumOfDistancesFast(const long *, size_t) noexcept;

template&lt;size_t Extent = std::dynamic_extent&gt;
[[nodiscard]]
constexpr long SumOfDistances(std::span&lt;const long, Extent&gt; pairs) noexcept
{
    static_assert(Extent == decltype(pairs)::extent);
    if constexpr (Extent != std::dynamic_extent) {
        static_assert(pairs.size() == Extent);
        static_assert(pairs.size() &gt; 0 &amp;&amp; pairs.size() % 2 == 0);
    } else {
#ifdef NDEBUG
        if (!(pairs.size() &gt; 0 &amp;&amp; pairs.size() % 2 == 0)) {
            [[unlikely]] __builtin_trap();
        }
#else
        assert(pairs.size() &gt; 0 &amp;&amp; pairs.size() % 2 == 0);
#endif
    }

    return SumOfDistancesFast(pairs.data(), pairs.size());
}

(Godbolt link with some example usages)

I think I should be able to make this example less repetitive while preserving the same safety properties, something like this:

#include &lt;span&gt;

#define precondition(_cond) /* static or dynamic assert */

extern long SumOfDistancesFast(const long *, size_t) noexcept;

template&lt;size_t Extent = std::dynamic_extent&gt;
[[nodiscard]]
constexpr long SumOfDistances(std::span&lt;const long, Extent&gt; pairs) noexcept
{
    precondition(Extent == decltype(pairs)::extent);
    precondition(Extent == std::dynamic_extent || pairs.size() == Extent);
    precondition(pairs.size() &gt; 0 &amp;&amp; pairs.size() % 2 == 0);

    return SumOfDistancesFast(pairs.data(), pairs.size());
}

In this updated example, the precondition macro doesn't care whether I'm statically or dynamically asserting and it will return me an error as soon as possible. Is it possible to write it?

答案1

得分: 1

precondition 宏可以定义为

#define precondition(cond) \
if constexpr (requires { typename std::bool_constant<(cond)>; }) \
  static_assert(cond); \
else \
  assert(cond);

如果条件是一个常量表达式,可以用作非类型模板参数,则使用 static_assert。否则,使用运行时的 assert 检查。

template<size_t Extent = std::dynamic_extent>
[[nodiscard]]
constexpr long SumOfDistances(std::span<const long, Extent> pairs) noexcept
{
    // 使用这个:
    precondition(Extent == decltype(pairs)::extent);
    precondition(Extent == std::dynamic_extent || pairs.size() == Extent);
    precondition(pairs.size() > 0 && pairs.size() % 2 == 0);

    return SumOfDistancesFast(pairs.data(), pairs.size());
}

演示(Clang 似乎无法通过 requires 子句来确定常量表达式,这似乎是一个 bug)

英文:

The precondition macro can be defined as

#define precondition(cond) \
if constexpr (requires { typename std::bool_constant&lt;(cond)&gt;; }) \
  static_assert(cond); \
else \
  assert(cond);

If the condition is a constant expression, which can be used as a non-type template argument, then use static_assert. Otherwise, a runtime assert check is used.

template&lt;size_t Extent = std::dynamic_extent&gt;
[[nodiscard]]
constexpr long SumOfDistances(std::span&lt;const long, Extent&gt; pairs) noexcept
{
    // With this:
    precondition(Extent == decltype(pairs)::extent);
    precondition(Extent == std::dynamic_extent || pairs.size() == Extent);
    precondition(pairs.size() &gt; 0 &amp;&amp; pairs.size() % 2 == 0);

    return SumOfDistancesFast(pairs.data(), pairs.size());
}

Demo (Clang does not seem to be able to determine constant expressions through the requires-clause, which seems to be a bug)

huangapple
  • 本文由 发表于 2023年7月20日 11:06:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76726425.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定