Время жизни временного с оператором присваивания для проверки и выравнивания alloca () - PullRequest
0 голосов
/ 24 апреля 2020

В процессе написания некоторого тестирования и анализа большой базы кода я должен заменить тысячи вызовов alloca () своим собственным методом. На моей целевой платформе alloca () не работает, если число равно нулю, поэтому мы хотим утверждать, что это так. Мы также хотим предоставить версию выравнивания, которая является одним вызовом. Тем не менее, alloca () имеет определенные c времена жизни, связанные с областью действия, и поэтому я хотел бы написать

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, и что существует множество решений, чтобы это исправить, но я надеюсь внедрить эти изменения в базу кода, не меняя ничего другого, а просто добавив немного диагностики.

Мне также просто любопытен этот шаблон c.

Это MSVC2019, CLANG и нишевые компиляторы систем реального времени.

1 Ответ

1 голос
/ 24 апреля 2020

Я бы предложил добавить вспомогательную функцию, которая выполняет ваш 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 s (вы могли бы assert на этом, но это было бы уродливее). Вы можете создать дополнительные вспомогательные функции, чтобы использовать другие нединамические c выделения (например, вы упомянули alloca_aligned в комментарии).

...