Полиморфная (чисто абстрактная) карта жертвует типом безопасности? - PullRequest
2 голосов
/ 28 октября 2011

Некоторый контекст: (Не стесняйтесь, пропустите вперед) У меня есть модуль, который обрабатывает сложные данные, но должен знать только его семантику.Данные могут рассматриваться как пакет: модуль должен только рассуждать о непрозрачной строке полезной нагрузки, но в конечном итоге он передаст все это парню, которому нужна дополнительная информация.Тем не менее, он должен ... «связывать» пакеты относительно некоторой неизвестной информации о пакете, поэтому я пришел к следующему:

struct PacketInfo {
  virtual void operator==(PacketInfo const&) const = 0;
  virtual void operator<(PacketInfo const&) const = 0;
  virtual ~PacketInfo() {}
};

class Processor {
  private:
    template <typename T> struct pless {
      bool operator()(T const* a, T const* b) const {
        assert(a && b);
        return *a < *b;
      }
    };
    // this is where the party takes place:
    std::map<PacketInfo const*,X,pless<PacketInfo> > packets;
  public:
    void addPacket(PacketInfo const*,X const&);
};

Теперь идея состоит в том, что пользователь реализует свою семантику PacketInfo ипроходит через мой класс.Например:
(пожалуйста, внимательно прочитайте конец вопроса перед ответом)

struct CustomInfo : public PacketInfo {
  uint32_t source;
  uint32_t dest;
  void operator==(PacketInfo const& b) const {
    return static_cast<CustomInfo const&>(b).dest == dest
    && static_cast<CustomInfo const&>(b).source == source;
  }
  // operator< analogous
};

В тот момент, когда я использую static_cast, большинство людей будут использовать dynamic_cast, но rtti деактивируется какпроект-политика.Конечно, я могу самостоятельно создать свою собственную информацию о типах, и я делал это раньше, но здесь вопрос не в этом.

Вопрос в том, как я могу получить то, что хочу (то есть, имея ключ карты беззная его содержание) без ущерба для безопасности типов, то есть без литья вообще?Я бы очень хотел, чтобы класс Processor не являлся шаблонным типом.

Ответы [ 3 ]

2 голосов
/ 28 октября 2011

Вы не можете.Вы либо знаете типы во время компиляции, либо проверяете их во время выполнения.Там нет серебряной пули.

1 голос
/ 28 октября 2011

Ответ в полном обобщении должен включать двойную рассылку.Идея состоит в том, что если у вас есть n различных подклассов PacketInfo, вам нужно n * (n - 1) / 2 реализации оператора сравнения.Действительно, что произойдет, если вы сравните CustomInfo с AwesomePersonalInfo?Это предполагает знание всей иерархии заблаговременно, и пример кода представлен в этом вопросе SO .

Если вы уверены, что вы можете применить карту с идентичными типами внутри(и поэтому вы уверены, что вам нужны только операторы n), тогда нет смысла иметь map<PacketInfo, X>.Просто используйте map<ConcretePacketInfo, X>.

Есть несколько способов сделать это.Здесь проще всего сделать шаблон Processor для типа пакета, возможно, сделав его наследником от класса BasicProcessor, если вы хотите «стереть» параметр шаблона где-нибудь и вычислить общий код.

Другойдешевое решение заключается в следующем: сохранить код как есть, но сделать Processor шаблоном, который определяет только соответствующий addPacket:

class BasicProcessor
{
private:
    template <typename T> struct pless 
    {
        bool operator()(T const* a, T const* b) const 
        {
            assert(a && b);
            return *a < *b;
        }
    };

protected:
    std::map<PacketInfo const*, X, pless<PacketInfo>> packets;
};

// You only need these lines in a public header file.
template <typename Packet>
class Processor : public BasicProcessor
{
public:
     void addPacket(Packet const* p, X const& x)
     {
         this->packets[p] = x;
     }
};

Это гарантирует, что вызывающая сторона будет манипулировать объектом Processor<CustomPacket> итолько добавьте правильный тип пакета.На мой взгляд, класс Processor должен быть классом шаблона.

Этот метод называется Тонкий шаблон идиома , где базовая реализация не являетсявведите safe (чтобы избежать раздувания кода относительно шаблонов), но добавьте тонкий слой шаблонов, чтобы восстановить безопасность типов на уровне интерфейса.

0 голосов
/ 28 октября 2011

Самая очевидная проблема, которую я вижу, состоит в том, что ваши operator< и operator== функции не постоянны. Таким образом, вы не можете вызвать их через указатель на const или ссылка на const. Они должны быть:

virtual voie operator==(PacketInfo const& other) const = 0;
virtual voie operator<(PacketInfo const& other) const = 0;

Кроме того, логически, если вы определите эти два, вы должны определить другое четыре. Я обычно справлюсь с этим путем определения полиморфного функция-член compare, которая возвращает значение <, == или > 0, в зависимости от того, является ли его this объект меньше, равен или больше чем другой объект. Таким образом, производные классы имеют только один Функция для реализации.

Кроме того, вам определенно необходим некоторый тип RTTI или двойная отправка в чтобы гарантировать, что оба объекта имеют одинаковый тип, когда вы сравнивая их (и как вы заказываете сравнение, когда они этого не делают).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...