Можно ли передать право собственности с void* на unique_ptr?

В настоящее время я использую функции dlopen для некоторого проекта плагина.
Этот дескриптор функции возвращает void*, а затем я сохраняю весь дескриптор на карту с именем handles:

void* handle = dlopen(path.c_str(), RTLD_LAZY);  
handles[file] = handle;

Моя цель - передать право собственности на карту, я думал о unique_ptr, но не уверен, что это вообще возможно.

Если это невозможно, какие у меня есть другие альтернативы?

void* сам по себе не имеет понятия владения, единственное «владение», о котором знает необработанный указатель, заключается в том, что вы должны его удалить, если вы этого не сделаете, то указатель ничем не владеет.
463035818_is_not_a_number 23.05.2019 09:56

Что именно здесь означает право собственности? IOW, какую очистку вы должны сделать, когда закончите с ручкой (если есть)?

Angew is no longer proud of SO 23.05.2019 10:05

Ручка должна быть закрыта. Поэтому я бы хотел, чтобы при очистке карты автоматически закрывались все дескрипторы (например, внутри средства удаления unique_ptr). В настоящее время мне приходится вызывать «закрыть» вручную, а также стирать, поэтому есть 2 операции.

Adrian 23.05.2019 10:07

Я не уверен, что вы действительно «владеете» пустотой*. Я имею в виду, как ты мог удалить что-то, размер которого не знаешь. Посмотрите на API, как вы должны правильно очищать дескриптор, и тогда, возможно, вы сможете обернуть это во что-то, с чем вы могли бы использовать семантику интеллектуального указателя.

Nathan Cooper 23.05.2019 10:08

@NathanCooper: да, я должен вызывать Close для каждого дескриптора, поэтому решение, возможно, состоит в том, чтобы инкапсулировать его в объект RAII и использовать его.

Adrian 23.05.2019 10:09

Я не совсем уверен, но пользовательские удаления может быть упрощенным способом достижения этого. Никогда не использовал их. Кто-то еще может ответить.

Nathan Cooper 23.05.2019 10:16
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
7
1 274
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Если я правильно понимаю, вы можете сделать что-то вроде

Определите функцию закрытия и псевдоним для типа указателя:

auto closeFunc = [](void* vp) {
  dlclose(vp);
};
using HandlePtr = std::unique_ptr<void, decltype(closeFunc)>;
std::map<std::string, HandlePtr> handles;

а затем создайте ручки и добавьте на карту:

void* handle = dlopen(path.c_str(), RTLD_LAZY); 
HandlePtr ptr( handle, closeFunc );

handles[file] = std::move( ptr );

Затем closeFunc будет вызываться, когда уникальный ptr выйдет за пределы области видимости.

Необработанный указатель можно предотвратить, объединив две строки выше:

HandlePtr handle(dlopen(path.c_str(), RTLD_LAZY), closeFunc );
handles[file] = std::move( handle );

При этом используется второй аргумент std::unique_ptr, который указывает используемую программу удаления.

PS: maps и unique_ptrs не работают как есть, вам могут понадобиться некоторые emplaces или перемещения в зависимости от используемого вами стандарта C++. Или используйте shared_ptr вместо этого.

PS: я понятия не имею, как очищается дескриптор, так как я не знаком с dlopen, я просто предположил, что используется dlclose.

CJCombrink 23.05.2019 10:12

Я пробовал это, но у меня всегда выдает ошибку "использование удаленной функции" при перемещении ptr внутри карты :/

Adrian 23.05.2019 15:34

Неважно, это работает, если я добавляю данные с помощью «emplace» :)

Adrian 23.05.2019 16:01

@Adrian Возможно, отредактируйте фрагменты кода в ответе, чтобы показать emplace, как он у вас работает.

CJCombrink 24.05.2019 11:13

std::unique_ptr особенно полезен для «обертывания» c-библиотек, которые работают с использованием ресурса, который необходимо удалить или закрыть по завершении. Это не только мешает вам забыть, когда удалять/закрывать ресурс, но и делает использование исключения ресурса безопасным, потому что ресурс правильно очищается в случае исключения.

Я бы, наверное, сделал что-то вроде этого:

// closer function to clean up the resource
struct dl_closer{ void operator()(void* dl) const { dlclose(dl); }};

// type alias so you don't forget to include the closer functor
using unique_dl_ptr = std::unique_ptr<void, dl_closer>;

// helper function that ensures you get a valid dl handle
// or an exception.    
unique_dl_ptr make_unique_dl(std::string const& file_name)
{
    auto ptr = unique_dl_ptr(dlopen(file_name.c_str(), RTLD_LAZY));

    if (!ptr)
        throw std::runtime_error(dlerror());

    return ptr;
}

И используйте его примерно так:

// open a dl
auto dl_ptr = make_unique_dl("/path/to/my/plugin/library.so");

// now set your symbols using POSIX recommended casting gymnastics
void (*my_function_name)();
if (!(*(void**)&my_function_name = dlsym(dl_ptr.get(), "my_function_name")))
    throw std::runtime_error(dlerror());

А затем напишите класс, чтобы также обернуть dlsym (поэтому shared_ptr кажется более подходящим, чтобы не закрываться, пока функция все еще активна).

Jarod42 23.05.2019 10:56

@Jarod42 Jarod42 Вы бы использовали Толькоstd::shared_ptr, когда ваш дизайн не позволяет узнать, какой из двух (или более) динамических объектов (которые содержат указатель на dl) первым выйдет из области видимости. Обычно std::unique_ptr является лучшим выбором, и его следует использовать, если вы не принужденный для использования std::shared_ptr (что должно быть довольно редко).

Galik 23.05.2019 11:16

Срок жизни my_function_name должен быть короче, чем dl_ptr. При правильном использовании share_ptrmy_function_name продлит срок службы dl_ptr и, следовательно, избегает использования «висячего» my_function_name. Но на самом деле, с unique_ptr вы можете написать полную оболочку library.so, чтобы гарантировать этот срок службы.

Jarod42 23.05.2019 11:23

@ Jarod42 Jarod42 Также несложно с «подходящим» использованием unique_ptr сделать то же самое - убедитесь, что ваши символы не переживут ваши dl. Вместо того, чтобы заставлять my_function_name содержать общую ссылку на ваш dl, вы можете сделать свой класс dl «обернутым» для всех функций, к которым dl предоставляет уникальный доступ.

Galik 23.05.2019 11:27

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

Jarod42 23.05.2019 11:36

Даже если лучше всего следовать ответу TheBadger. Помните, что в C++ нет концепции владения, C++ предоставляет классы для имитации этого и эффективного использования RAII, но на самом деле у вас нет никаких гарантий от языка, чтобы гарантировать владение ресурсом, даже с unique_ptr, например:

{
  auto p_i = new int{5};
  auto up_i = std::unique_ptr<int>{p_i};
  *p_i = 6; //nothing in the language prevent something like this
  assert(*up_i == 6); //the unique_ptr ownership is not assured as you can see here
  delete p_i; //still not illegal
} //at run time, RAII mechanism strikes, destroy unique_ptr, try to free an already freed memory, ending up to a core dumped

У вас даже есть функция-член get в классе unique_ptr, которая дает вам возможность испортить вашу память, как вы могли бы это делать с необработанными указателями.

В С++ нет механизма для обнаружения нарушения «права собственности» во время компиляции, возможно, вы можете найти какие-то инструменты для его проверки, но этого нет в стандарте, это всего лишь вопрос рекомендуемых практик.

Таким образом, более уместно и точно говорить об «управляемой» памяти в C++, а не о «владении».

«Управляемая память» обычно относится к сборщику мусора. Мы действительно используем термин "владение" в C++ (и да, мы все еще можем делать мусор в C++ при неправильном использовании).
Jarod42 23.05.2019 11:27

Мы используем этот термин, да, как способ выразить тот факт, что, вообще говоря, вы не будете изменять память, управляемую unique_ptr, и, таким образом, «право собственности» на ресурс уважается, но этот вид владения является только Что касается рекомендуемых практик, как я уже сказал, в языке нет механизма проверки во время компиляции любых нарушений прав собственности, как в rust. Я думаю, очень важно помнить об этом.

Steranoid 23.05.2019 11:37

Никогда не владейте собственными указателями. Используйте ручки. Не существует разумного механизма, который предотвратит двойное освобождение собственного указателя, использование после освобождения и т.д. std::unique_ptr<HANDLE, handle_release> в один конец.

Chef Gladiator 05.08.2021 12:00

Другие вопросы по теме