Я уже год работаю разработчиком программного обеспечения в отделе компьютерного зрения компании. Моя основная работа - интеграция стороннего программного обеспечения в фреймворк, поэтому я обычно заканчиваю писать библиотеки оболочек, потому что большая часть этого стороннего программного обеспечения не работает так, как мы хотим, чтобы оно работало (не потокобезопасное, боль в a * * использовать и т. д.).

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

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

1
tr9sh 15 Июн 2009 в 04:44

2 ответа

Лучший ответ

Я процитирую здесь на днях ответ на другой вопрос :

  1. Пройдет ли ваш текущий метод тестирование?
  2. Достаточно ли быстро?

Если да, продолжайте делать то, что делаете.

В качестве альтернативы

Просто убедитесь, что ваш новый API включает как предполагаемые функциональные возможности, так и обычные или случайные функциональные возможности исходного. Также убедитесь, что он представляет собой повторную презентацию, «соответствующую назначению». Взгляните на оболочку C ++ для библиотек C в проектах FOSS, таких как GTK / GTK для C ++ (которая просто обертывает первое).

Если API не работает, исправьте его и отправьте патч ... свяжитесь с третьими сторонами (я предполагаю, что наличие доступа к источнику означает, что они не будут возражать) ... Вы можете переписать некоторые из их API должен быть «дружественным к упаковке» и предлагать внести некоторые изменения. Если есть проблема, пусть ее устранят.

Ничего особенного, просто оберните A с помощью B и убедитесь, что B выполняет то, что должен был делать, или для чего используется.

5
Aiden Bell 15 Июн 2009 в 05:08
Спасибо, это как-то укрепило мою уверенность;) Обычно я не сталкиваюсь с какими-либо проблемами скорости или семантики, но обычно код просто не такой чистый, как мне хотелось бы. но я думаю, что другого пути нет, так как у меня нет источника сторонней библиотеки.
 – 
tr9sh
15 Июн 2009 в 05:13

Единственное, что я могу добавить к ответу Эйдена, - это то, что вам также следует заменить код, требующий явной инициализации и завершения, методами RAII. Когда я сталкивался с необходимостью создания фасада над API-интерфейсами, мне всегда казалось, что я натыкаюсь на класс, который выглядит так:

struct ADVERTISER {
    /* a bunch of members here */
};

void adv_Initialize(ADVERTISER *adv, /* a bunch of arguments */);
void adv_DoStuff(ADVERTISER *adv);
void adv_Terminate(ADVERTISER *adv);

Я видел это завернутым в класс C ++ следующим образом:

namespace wrapper {
  class Advertiser {
  public:
      Advertiser(): inited_(false) {}
      void initialize(/* a bunch of arguments */) {
        terminate();
        adv_Initialize(&adv_, ...);
        inited_ = true;
      }
      void doStuff() {
        validate();
        adv_DoStuff(&adv_);
      }
      void terminate() {
        if (inited_) {
            adv_Terminate(&adv_);
            inited_ = false;
        }
      }
  protected:
      void validate() {
        if (!inited_) {
            throw std::runtime_error("instance is not valid");
        }
      }
  private:
      ADVERTISER adv_;
      bool inited_;
  };
}

Проблема в том, что класс Advertiser на самом деле не делает API более простым в использовании или даже чище ИМХО. Если вы столкнетесь с подобными случаями, то:

  1. Используйте полностью параметризованный конструктор, чтобы убедиться, что недопустимые экземпляры не существуют
  2. Очистите все ресурсы в деструкторе
  3. Напишите конструктор копирования и оператор присваивания, если они имеют смысл или сделайте их частными и не реализуйте их.

Моя цель - убедиться, что любой API, который я представляю / создаю / упаковываю, работает с нашим существующим стилем кодирования. Я также пытаюсь преобразовать API в более объектно-ориентированный стиль, чем он может быть в настоящее время. Я видел несколько примеров того, что я называю объектно-ориентированным C , подобных тому, который я представил выше. Если вы хотите, чтобы они действительно вписывались в C ++, сделайте их действительно объектно-ориентированными и воспользуйтесь тем, что дает вам C ++:

  • Будьте осторожны при управлении любыми переменными состояния.
  • Если такие действия, как копирование, не имеют смысла, скройте их.
  • Если есть вероятность утечки ресурсов, найдите способ предотвратить ее (обычно с помощью RAII).
  • Ограничьте создание экземпляров с помощью конструкторов, чтобы исключить недопустимые экземпляры и другие крайние случаи.
2
Community 23 Май 2017 в 13:27
Спасибо, я думаю, что это очень хороший совет, особенно для меня, так как у меня сильный опыт работы с C, поэтому я новичок в C ++.
 – 
tr9sh
15 Июн 2009 в 11:54
Вы очень кстати. Я потратил кучу лет на C на встраиваемых платформах, прежде чем переключился на C ++, так что я определенно могу понять. Я обнаружил, что это имеет огромное значение, если вы сделаете свои обертки более подходящими для C ++ - таким образом вы получите что-то, обернув другой API.
 – 
D.Shawley
15 Июн 2009 в 19:01