2014-12-28 4 views
1

Хотя это должно быть тривиальным вопросом, мне так и не удалось найти ответ. В C API существует множество функций, которые в качестве аргументов принимают указатели и указатели на указатели. Как использовать интеллектуальные указатели PROPERLY в качестве аргументов с API-интерфейсом C.Использование интеллектуальных указателей C++ 11 в качестве аргументов функции C

Вот пример, который я хотел бы преобразовать с помощью зЬй :: unique_ptr:

FMOD_SYSTEM* system = nullptr; 
result = FMOD_System_Create(&system); // Create the main system object 

FMOD_SOUND* musicStream; 
result = FMOD_System_CreateSound(system, 
           musicStreamPath, 
           FMOD_CREATESTREAM, 
           nullptr, 
           &musicStream); 

Ссылка: FMOD_System_Create FMOD_System_CreateSound

Я начинаю декларировании умные указатели как:

std::unique_ptr<FMOD_SYSTEM> system = nullptr; 
std::unique_ptr<FMOD_SOUND> musicStream = nullptr; 

Вот ошибки компилятора, если я использую .get():

не может преобразовать 'зЬй :: unique_ptr :: указатель {ака FMOD_SOUND *}' в 'FMOD_SOUND **' для аргумента '5' «FMOD_RESULT FMOD_System_CreateSound (FMOD_SYSTEM *, Const символ *, FMOD_MODE, FMOD_CREATESOUNDEXINFO *, FMOD_SOUND **) ' musicStream.get());

            ^
+1

Используйте [ '.get()'] метод (http://en.cppreference.com/w/cpp/memory/unique_ptr/get), чтобы получить базовый указатель? – Cornstalks

+0

Не работает только с .get(). Там, кажется, больше, но я пока не могу понять. – Zingam

+0

Здесь есть реальный вопрос и ответ. Я работаю над этим. – Quuxplusone

ответ

3

Ваша проблема в том, что, что C API ожидает, чтобы передать его адрес указатель на FMOD_SYSTEM, так что API может заполнить этот указатель с результатом - то есть, он принимает FMOD_SYSTEM* как out-parameter.

В C++, идиоматических способ сделать это было бы передать ссылка на а (умный?) Указатель на FMOD_SYSTEM, то есть, где C API является

FMOD_RESULT FMOD_System_Create(FMOD_SYSTEM **system); 

FMOD_SYSTEM *system; 
result = FMOD_System_Create(&system); 

С ++ API будет be

FMOD_RESULT FMOD_System_Create(std::unique_ptr<FMOD_SYSTEM> &system); 

std::unique_ptr<FMOD_SYSTEM> system; 
result = FMOD_System_Create(system); 

Однако, существует большая проблема с этим C++ API! Проблема в том, что , создавая a FMOD_SYSTEM и , обертывая его в unique_ptr - это отдельные проблемы, и их не следует пюре вот так. Например, что делать, если я делаю что-то умное с потоками и действительно нуждаюсь в управлении shared_ptr, а не просто unique_ptr? Мне нужно создать unique_ptr, чтобы передать его как out-parameter, а затем std::move в shared_ptr? Это и уродливое, и (микро-) неэффективное.

std::unique_ptr<FMOD_SYSTEM> fake_system; 
result = FMOD_System_Create(fake_system); 
std::shared_ptr<FMOD_SYSTEM> system(std::move(fake_system)); 

Ответ должен признать, что корень проблемы заключается в самих из-параметров, и что решение значение семантики.Идиоматический C синтаксис ++ мы хотим написать это

auto system = std::make_unique<fmod_system>(); 

и способом, которым мы получаем, что синтаксис является подведение сырых указателей на C API в классах значений:

class fmod_system { 
    FMOD_SYSTEM *ptr; 
    fmod_system() { 
     auto result = FMOD_System_Create(&ptr); 
     if (result != FMOD_OK) { 
      ptr = nullptr; 
      throw something; 
     } 
    } 
    fmod_system(fmod_system&&) = default; 
    fmod_system& operator=(fmod_system&&) = default; 
    fmod_system(const fmod_system&) = delete; 
    fmod_system& operator=(const fmod_system&) = delete; 
    ~fmod_system() { 
     auto result = FMOD_System_Release(ptr); 
     assert(result == FMOD_OK); // destructors shouldn't throw: use your best judgment here 
    } 
}; 

И на самом деле в этом указывают наши абоненты могут уронить unique_ptr запутывания и просто написать

fmod_system system; 

если не они действительно нужно лишний слой семантики указателя по какой-то причине.

+0

В этом контексте. Есть ли общее правило, как использовать C API в правильном стиле C++? – Zingam

+1

@ Zingam: Горячая фраза прямо сейчас - «семантика значений»; старшая и почти синонимичная фраза - «RAII». В обоих случаях должны появиться некоторые хорошие правила. Если это не поможет, я бы рекомендовал опубликовать другой вопрос и сделать его максимально узким. И/или попробуйте написать код самостоятельно, а затем отправить его на [CodeReview Stack Exchange] (http://codereview.stackexchange.com) для обратной связи! – Quuxplusone

+0

Хотя оба термина являются хорошими поисковыми подсказками, семантика значений и RAII являются двумя в основном ортогональными понятиями. Сеантика значения примерно (упрощена), чтобы ваши типы вели себя как 'ints' (в частности, в отношении семантики копирования).RAII - это метод, позволяющий автоматически получать исходные материалы. – MikeMB

2

Простой ответ: C++ Умные указатели используются для самостоятельных объектов, которые вы создадите с помощью new и delete. Они не строятся для указателей на объекты, созданные внутренне сторонними функциями API стиля C и должны быть выпущены другой функцией C Style API. В вашем случае вам нужно будет позвонить System::release, а не delete, поэтому использование умного указателя может вызвать большие проблемы.

У вас есть три возможности:

  1. Просто остаться с C стиль и заботиться о вызове освободить себя. (Я предпочел бы это)
  2. Написать свою собственную C++ класс обертки - как Quuxplusone предложил
  3. Изменения C++ смарта-указателя с настраиваемым «распределителем» и «Deleter» (Это сложное, теоретическое и не рекомендуется)
+0

Nothing теоретически об использовании C++ smpartpointer с costum deleter (И, возможно, это тоже не слишком сложно). – MikeMB

3

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

Однако, чтобы ответить на ваш вопрос, «самый чистый» (но все же уродливый способ), я могу думать о том, чтобы передать исходный указатель на смарт-указатель с пользовательским удалением после создания объекта.

struct FMOD_SYSTEM_Deleter {  
    void operator()(FMOD_SYSTEM* sys) { 
     if (sys != nullptr) { 
      FMOD_System_Release(sys); 
     } 
    } 
} 

FMOD_SYSTEM* tsys = nullptr; 
result = FMOD_System_Create(&tsys); 
std::unique_ptr<FMOD_SYSTEM,FMOD_SYSTEM_Deleter> system(tsys); 
Смежные вопросы