Несколько аргументов для размещения C++ нового «конструктора»

Чтобы выполнить некоторое пользовательское отслеживание памяти (предотвращение утечки, обнаружение повреждения), мне приходится использовать новое размещение для создания объектов С++, что отлично работает, но я изо всех сил пытаюсь понять, как я могу передать аргументы конструктору, поскольку он вызывается из макроса (поэтому файл + строка могут быть предоставлены автоматически).

Функция:

template <typename T>
T*
cpp_new(
    const char *file,
    size_t line
)
{
    T   *n = (T*)tracked_allocate(&memory_context, sizeof(T), file, line);

    if ( n )
    {
        construct(n);
    }
    else
    {
        throw std::bad_alloc();
    }

    return n;
}

Вызывается через макрос:

#define new_object(type)    cpp_new<type>(__FILE__, __LINE__)

Размещены новые:

template <typename T>
void
construct(
    T *obj
)
{
    obj = new (obj) T;
}

Макросы va_list охватывают расширение для переменного количества аргументов, только я не хочу предоставлять количество аргументов, которое имеет конструктор, удаляя va_arg(), и не могу использовать va_start(), так как он ожидает формат.

Это пролетело мимо меня: http://www.drdobbs.com/cpp/calling-constructors-with-placement-new/232901023?pgno=2

Можно ли как-нибудь использовать __VA_ARGS__ из new_object и передать их в функцию построения? У каждого объекта есть только один конструктор, но есть много разных типов объектов, принимающих разные параметры, поэтому я хочу убрать как можно больше ручного обслуживания.

Или есть лучший способ сделать то, что я пытаюсь сделать!


person ZXcvbnM    schedule 01.10.2012    source источник


Ответы (1)


Вы не должны иметь дело с конструкцией-если-распределение-успешно. Это работа new-выражения. Он делает это правильно, как операцию, подобную транзакции: либо все завершается успешно, либо выполняется очистка перед распространением исключения.

Итак, освобожденный от этой ответственности, работа вашего макроса состоит в том, чтобы делать то, что могут делать только макросы, а именно подбирать имя файла и строку.

Эти элементы могут/должны быть переданы в функцию распределителя, которая технически является "новым размещением", хотя здесь она не будет создаваться на месте: это просто новое размещение потому что у него есть дополнительные пользовательские аргументы, например:

bool hopefully( bool const c ) { return c; }

template< class X >
bool throw_( X const& x ) { throw x; }

void* operator new( size_t const size, char const* const filename, int const linenum )
{
    void* const p = tracked_allocate( &memory_context, size, filename, linenum );
    hopefully( p != 0 )
        || throw_( std::bad_alloc() )
    return p;
}

Вам необходимо определить соответствующую функцию освобождения места размещения, иначе выражение new не сможет освободить место, когда конструктор выдает:

void operator delete( void* const p )
{
    // Your custom deallocation.
}

void operator delete( void* const p, char const*, int )
{
    ::operator delete( p );
}

Теперь ваш макрос просто должен предоставить соответствующие аргументы размещения, например:

#define SOURCE_LINE_INFO __FILE__, __LINE__

Тогда вы можете просто сказать, например,

new (SOURCE_LINE_INFO) MyType( arg1, arg2, arg3 )

Для более удобного решения рассмотрите возможность определения struct для хранения имени файла и номера строки. Затем макрос сводится к созданию экземпляра этого типа и может использоваться более широко. В частности, его можно использовать для регистрации вызовов.

person Cheers and hth. - Alf    schedule 01.10.2012
comment
Впечатляющий! Только небольшое изменение по сравнению с «нормально выглядящим синтаксисом», которое я стремился сохранить, и работает при удалении отладочной информации без изменения источника. Спасибо :) -- отредактируйте: вам просто не хватает точки с запятой в конце перед возвратом в операторе new! - person ZXcvbnM; 01.10.2012