Ресурс-прокси класс… C++ лучшие практики

У меня есть общая проблема - написать оболочку C++ для библиотеки C… мое основное хранилище - указатели C-Struct, которые всегда представляют класс C++, содержащий только ОДНУ поданные данные.

class MyClassC {
  struct MyStructS * hdl
  ...
}

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

Значение класса всегда возвращает объект, а НЕ указатель:

класс значений имеет отношение ONE к MANY между MyStructS и MyClassC

MyClassC myMethod () {
  ...
  return MyClassC(struct MyStructS *...);
}

Деструктор класса значений никогда не освобождает указатель struct MyStructS и в основном пуст

Деструктор значения - это статический метод или метод, обычно называемый destry или delete.

MyClassC::delete() {
   DestroyMyPointer(&hdl)
}
...
MyClassC obj = {...};
...
obj.delete();
...

класс значения также является аргументом для методов значения:

some_proc_or_method (MyClassC arg1, MyClassC arg2, …) {
   ...
} 

и есть другой вопрос:

Как создать аргументы по умолчанию для аргумента класса значения?

some_proc_or_method (MyClassC arg1, MyClassC arg2 = MyClassC {...} ???? ) {
   ...
} 

Указатель класса всегда возвращает указатель объекта:

класс указателя имеет отношение ОДИН к ОДНОМУ между MyStructS и MyClassC

MyClassC* myMethod () {
  ...
  return this or getThisFrom(myStructS_pointer)
}

Деструктор класса указателя всегда освобождает указатель struct MyStructS

~MqClassC () {
   DestroyMyPointer(&hdl)
 }

...
MyClassC* obj = new MyClassC(...);
...
delete obj;
...

1 ответ

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

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

#include <memory>
#include <CLibAPI.h>

namespace CPPWrapC {
  namespace detail {
    struct DestroyMyPointer {
      void operator()(MyStructS *hdl) {
        ::DestroyMyPointer(&hdl);
      }
    };
  }

  using Handle = std::unique_ptr<MyStructS, detail::DestroyMyPointer>; 


  Handle myMethod() {
    MyStructS *ret = /* init it */;
    return Handle{ret};
  }

  inline void some_proc_or_method(Handle const& hdl) {
    c_lib_some_proc_or_method(hdl.get());
  }
}

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

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

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