Можно ли обернуть бустерные розетки с помощью Pimpl?

в проекте мы хотим обернуть сокет Boost Asio таким образом, чтобы класс using или обертка .h не включали заголовки boost.

Обычно мы используем указатели и форвардные объявления для обернутых классов.

Заявление Foward:

namespace boost
{
  namespace asio
  {
    namespace ip
    {
      class udp;
    }
  }
}

А затем объявим сокет:

  scoped_ptr<boost::asio::ip::udp::socket> socket_;
  scoped_ptr<boost::asio::ip::udp::endpoint> receiveEp_;

(Если вы не знаете scoped_ptr, игнорируйте его, проблема аналогична стандартному указателю *.)

Но это дает ошибку компилятора:

error C2027: use of undefined type 'boost::asio::ip::udp'

Я понимаю, что это потому, что udp на самом деле не пространство имен, а сам класс. Мы хотим использовать только внутренний класс, есть идеи?


person Tarnschaf    schedule 28.10.2009    source источник


Ответы (2)


С внутренними типами ваш единственный вариант - все обернуть. Скрыть сами указатели с ограниченной областью видимости внутри объявленного вперед класса. Пользователи будут видеть только ваш API и передавать ваши собственные объекты вместо объектов Boost.

В вашем примере, хотя scoped_ptr выглядят как объявления частных членов, вы можете обойтись простым:

// header
class SomeClass 
{ 
public:
    SomeClass();
    // stuff   
private: 
    scoped_ptr<class pimpl_bla> _m; 
};

// source
class pimpl_bla
{
public: 
    scoped_ptr<boost::asio::ip::udp::socket> socket_;
};

SomeClass::SomeClass() 
    :_m(new pimpl_bla)
{ 
}
person Eugene    schedule 28.10.2009
comment
То есть мне пришлось бы дважды определять все общедоступные функции .. и SomeClass просто передавал бы их pimpl_bla? - person Tarnschaf; 28.10.2009
comment
Зависит от того, что имеет смысл. Я обычно использую класс pimpl в качестве корзины-члена данных (также обычно это структура), к которой класс хоста обращается точно так же, как если бы они были его собственными. Но я ленив. :) - person Eugene; 28.10.2009
comment
janm ниже говорит о том же самом, кстати. - person Eugene; 28.10.2009
comment
да, и вы оба правы, но я не хотел оборачивать каждую функцию, потому что это всего лишь внутренний уровень, а не API. Но если это единственный способ избавиться от транзитивных зависимостей буста .. - person Tarnschaf; 28.10.2009
comment
Затем вы можете пойти (несколько) ленивым путем и использовать это - ›_ m-› data = 0 вместо this- ›data = 0 и сохранить все публичные функции в публичном классе. - person Eugene; 28.10.2009
comment
(если ускорение не входит в состав интерфейса открытого класса ...) - person Eugene; 28.10.2009

Если вы используете pimpl, почему вы помещаете переменные-члены в свой заголовок? Используются ли в вашем интерфейсе определения типов сокетов и конечных точек? Если они не являются частью вашего интерфейса, весь смысл идиомы pimpl в том, что вы не определяете переменные-члены класса в файле заголовка; это детали реализации.

Я ожидаю что-то вроде этого в заголовочном файле:

// ...

class MyClass {
public:
    MyClass();
    // .. Other member functions

private:
    struct Imp;
    boost::shared_ptr<Imp> m_imp;   // This is the only member variable.
};

А затем в вашем файле реализации:

#include <boost/asio/whatever.hpp>

struct MyClass::Imp
{
    scoped_ptr<boost::asio::ip::udp::socket> socket_;
    scoped_ptr<boost::asio::ip::udp::endpoint> receiveEp_;

    // Remainder of implementation class ...
};

// Other implementation details.

Чтобы ответить на ваш конкретный вопрос, типы, которые вы пытаетесь использовать, являются typedef в классе asio udp, поэтому компилятор должен увидеть определение этого класса, чтобы использовать его.

person janm    schedule 28.10.2009