首页 > 解决方案 > 使用赋值运算符检查和对齐 alloca() 的临时生命周期

问题描述

在编写大型代码库的一些测试和分析过程中,我不得不用自己的方法替换对 alloca() 的数千次调用。在我的目标平台上,如果数字为零,alloca() 将失败,因此我们要断言是这种情况。我们还希望提供一个单一调用的对齐版本。但是, alloca() 具有与范围相关的特定生命周期,所以虽然我想写

void * CheckAndAllocate( size_t sizeInBytes )
{
  assert( sizeInBytes > 0 );
  return alloca(p); // not safe; allocation goes out of scope on return
}

void * p = CheckAndAllocate( sizeInBytes );

显然,这不是一个选项,因为 alloca() 不会超过 CheckAndAllocate()。

在解决这个问题的过程中,我使用临时写了这个替代方案:

struct CheckSize
{
  inline CheckSize( size_t size ) { assert( size > 0 ); }
  inline void * operator=(void* other) { return other );
}

#define my_alloca(size) CheckSize(size) = alloca(size)

void foo(size_t size)
{
  void * p = my_alloca(size);
  // becomes
  void * p = CheckSize(size) = alloca(size);

  // ... use p locally for work
}

此外,我还将对齐版本定义为:

struct CheckSizeAndAlign
{
  size_t _align;
  inline CheckSizeAndAlign( size_t size, size_t align ) : _align(align) { assert( size > 0 ); }
  inline void * operator=(void* other) { return AlignUp(other, _align  ); }

#define my_alloca_aligned( size, align ) CheckSizeAndAlign(size, align ) = alloca(size + align)

void foo(size_t size, size_t alignment)
{
  void * p = my_alloca_aligned( size, alignment);
  // becomes
  void * p = CheckSizeAndAlign( size, alignment) = alloca( size + alignment )

  // ... use p locally for work
}

我的问题是 - 鉴于 alloca 返回的值是通过临时的,这是否因为任何类型的作用域而违反了 alloca 的分配?

我认识到 alloca 存在缺陷,并且有多种解决方案可以解决此问题,但我希望将这些更改注入代码库而不更改任何其他内容,而只是添加一些诊断。

我也只是对这种特定模式感到好奇。

这是 MSVC2019、CLANG 和小众实时系统编译器。

标签: c++variable-assignmentasserttemporaryalloca

解决方案


我建议添加一个辅助函数来执行您的assert、 调用alloca并将分配的内存传递给回调。这是我在实现中的第一遍:

#include <alloca.h>
#include <cassert>
#include <iostream>
#include <utility>

namespace {
    template <typename FN, typename ...ARGS>
    auto alloc_helper(std::size_t size, FN fn, ARGS && ...args) {
        assert(size > 0);
        auto p = ::alloca(size);
        return fn(p, std::forward<ARGS>(args)...);
    }
}

int main() {
    auto weird_add = [](void * p, auto init, auto other) {
        auto ip = reinterpret_cast<int *>(p);
        *ip = init;
        return *ip + other;
    };

    std::cout << alloc_helper(sizeof(int), weird_add, 10, 17) << '\n';
    return 0;
}

此解决方案的优点是只需键入一次大小,而且返回的指针的alloca范围恰好是您想要使用它的范围;除非回调return是它(你可以assert这样做,但它会更丑陋)。您可以制作其他辅助函数来使用其他非动态分配(例如,您alloca_aligned在评论中提到)。


推荐阅读