Есть ли идеоматический c способ избежать использования Naked New? - PullRequest
3 голосов
/ 07 мая 2020

В моем приложении мне нужно установить обратные вызовы, единственная ответственность которых заключается в обновлении состояния, связанного с завершенным событием в отдельном объекте, чтобы его можно было запросить позже. Однако из-за того, как разработан API, я не могу гарантировать, что другой объект по-прежнему будет принадлежать к моменту завершения события, поэтому мне нужно сохранить указатель на этот объект, и поскольку API обратного вызова имеет C на основе, я в конечном итоге сохраняю необработанный указатель на интеллектуальный указатель, и это самый уродливый фрагмент кода, который я видел *.

* В любом случае, в последние несколько часов ... .

Итак, это то, что я написал для выполнения sh this:

event.setCallback(CL_COMPLETE, [](cl_event event, cl_int, void* ptr) {
    auto ptr_ptr = static_cast<std::weak_ptr<render_future::shared_state>*>(ptr);
    if(auto shared_ptr = ptr_ptr->lock()) {
        auto & shared_state = *shared_ptr;
        std::lock_guard lock{ shared_state.mutex };
        shared_state.event_state[event] = true;
    }
    delete ptr_ptr;
}, new std::weak_ptr<render_future::shared_state>(future.state));

В частности, я возражаю против собственного использования new std::weak_ptr<render_future::shared_state>(future.state), который мне кажется своего рода антипаттерном: использование голых new и delete в сочетании с интеллектуальными указателями.

Проблема, однако, в том, что, поскольку обратный вызов должен быть указателем на функцию, мое лямбда-выражение не может копировать или ссылаться на другие объекты, и единственный способ получить объект shared_state внутри лямбда - это передать его указатель; и снова, поскольку я не могу гарантировать, что его время жизни не истекло, мне нужно принять его в виде указателя на weak_ptr, чтобы им можно было управлять, если (и только если) объект все еще существует.

Итак, в конечном итоге, мой вопрос: существует ли идеоматический c способ передать shared_state в этот обратный вызов, где

  1. я могу проверить, чтобы убедиться, что объект все еще существует, а
  2. Также я исключаю использование голых звонков new и delete?

Ответы [ 2 ]

3 голосов
/ 07 мая 2020

В лямбда-выражении есть очевидное улучшение, которое возможно, если просто использовать unique_ptr:

auto callback = [](cl_event event, cl_int, void* ptr) {
    std::unique_ptr<std::weak_ptr<render_future::shared_state>> ptr_ptr{ static_cast<std::weak_ptr<render_future::shared_state>*>(ptr)};
    if(auto shared_ptr = ptr_ptr->lock()) {
        std::lock_guard lock{ shared_state->mutex };
        shared_state->event_state[event] = true;
    }
}

Что касается создания, вы, возможно, можете использовать std::unique_ptr<>::release().

auto ptr = std::make_unique<std::weak_ptr<render_future::shared_state>>(future.state);

event.setCallback(CL_COMPLETE, callback, ptr.release());

Но поскольку вы вызываете функцию C с лямбдой без захвата, это не похоже на то, что здесь есть неожиданные исключения, от которых нужно защищаться, поэтому спорный вопрос, является ли это реальным улучшением по сравнению с тем, что вы делаете в настоящее время.

0 голосов
/ 07 мая 2020

Вы можете сохранить интеллектуальный указатель на объект внутри карты с указателем, не являющимся владельцем, в качестве ключа. Обратный вызов может стереть указатель с карты, тем самым освободив владение. Карта должна пережить обратный вызов. При необходимости вы можете прибегнуть к хранилищу stati c.

Благодаря уникальному владению вы можете вместо этого сохранить сам объект на карте напрямую.

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

...