Как решить циклическую зависимость шаблона Boost::BGL‹-›класса?

У меня проблема с использованием списка смежности графической библиотеки Boost. Кажется, это проблема циклической зависимости: у меня есть typedef T шаблона, который использует некоторый класс A. Кроме того, A хранит указатель на объект типа T. Теперь компилятор говорит мне, что T не называет тип.

Вот выдержки из моих более конкретных файлов:

//graphdefinitions.hpp
#include "lane.hpp"
#include "tie.hpp"

typedef boost::adjacency_list<boost::listS, boost::listS, 
                              boost::directedS, Tie, Lane> Map;
typedef boost::graph_traits<Map>::edge_descriptor edge_descriptor;

//lane.hpp
#include "graphdefinitions.hpp"
class Lane {
    ...
    edge_descriptor *left, *right;
};

//tie.hpp
//no important includes here
class Tie {
    ...
};

Как решить эту проблему зависимости/порядка включения?

ДРУГОЕ РЕДАКТИРОВАТЬ: мне просто пришла в голову идея, что тип edge_descriptor может быть примитивным, например, int. Это решило бы проблему, потому что я мог бы заменить дескрипторы edge_descriptors Lane простыми переменными int и, таким образом, удалить включение graphdefinitions.hpp в tie.hpp. К сожалению, моя идея была cra*, и я должен найти другое решение. Типы Edge_descriptor, кажется, существуют по какой-то причине...


person Bastian    schedule 20.11.2010    source источник


Ответы (4)


Вы циклически включили заголовки. Переулок включает определения графов, в том числе переулок, включающий определения графов и т. д. Это причина вашей проблемы.

Изменить: я понял, что это уже упоминалось в ОП. Решением этой проблемы является PIMPL.

Изменить: на самом деле я бы поместил typedef в класс Lane. Это должно решить проблему самым аккуратным образом.

person Puppy    schedule 20.11.2010
comment
Точно! Но возникает вопрос: какова (работающая!) альтернатива этому подходу? Я также уже пытался просто объявить Tie и Lane внутри graphdefinitions.hpp, что также не было принято компилятором. - person Bastian; 20.11.2010
comment
@Bastian: используйте PIMPL для реализации Lane. - person Puppy; 20.11.2010
comment
@DeadMG: Пожалуйста, взгляните на мой последний пост. - person Bastian; 20.11.2010
comment
@Bastian: Вы должны отметить мой ответ. У вас должна быть возможность просто выделить место для класса laneside (например, char[sizeof(void*)] и поместить его в конструктор. Однако это совершенно невозможно поддерживать. - person Puppy; 21.11.2010
comment
Необходимо хорошо продумать ремонтопригодность. Поэтому я просто придерживаюсь своего текущего решения, независимо от его уродства или дополнительного потребления памяти. Может быть, когда-нибудь я найду лучше. - person Bastian; 25.11.2010
comment
@Bastian: Почему бы просто не поместить typedef в класс Lane? - person Puppy; 25.11.2010
comment
@DeadMG: скоро попробую. Я просто имею дело с другой проблемой, связанной с использованием связанных свойств вместе с картами свойств: stackoverflow.com/questions/4276666/ - person Bastian; 25.11.2010

В BGL есть плохо документированный класс признаков, который дает типы дескрипторов вершин и ребер для графа adjacency_list без необходимости знать типы свойств. Он разработан именно для вашего варианта использования. Посмотрите раздел «Связанные типы» на http://www.boost.org/doc/libs/1_45_0/libs/graph/doc/adjacency_list.html и обратите внимание на два определения для vertex_descriptor и edge_descriptor; вы можете использовать версии, полученные из adjacency_list_traits, в определениях ваших пакетов свойств, не вызывая циклического определения.

person Jeremiah Willcock    schedule 21.01.2011

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

person Diego Sevilla    schedule 20.11.2010
comment
Итак, вы предлагаете мне включить заголовочные файлы используемых типов? Если я это сделаю, компилятор начнет жаловаться на Map: не был объявлен во всех файлах, использующих Map. - person Bastian; 20.11.2010
comment
Упорядочение таких типов зависимостей иногда бывает затруднительным. Вы должны опубликовать, какие типы определены в каких файлах заголовков и как они включены, чтобы получить лучший ответ. - person Diego Sevilla; 20.11.2010

@DeadMG: сейчас я использовал подход, подобный PIMPL, и думаю, что это решило мою проблему.

Так что же я сделал? Я изменил свой класс Lane, чтобы он выглядел так:

//lane.hpp
#include "graphdefinitions.hpp"

class LaneSide;
class Lane {
public:
    const LaneSide getLeft() const;
    const LaneSide getRight() const;
    ...
private:
    LaneSide *left;
    LaneSide *right;
    ...
};

И LaneSide-класс, который практически является косвенным и содержит тип значения, который я не мог перенаправить, объявить внутри lane.hpp, выглядит так:

//laneside.hpp
class LaneSide
{
    edge_descriptor* edge;
};

Кажется, это обманывает компилятор, как я и собирался. Так что спасибо за подсказку DeadMG. Что мне было интересно: возможно ли хранить объект LaneSide внутри класса Lane не как указатель, а как реальный объект? Сначала я попробовал это, но компилятор пожаловался на конструкцию. И мне также интересно, есть ли способ избежать дополнительного потребления памяти. Когда мой график станет достаточно большим, это может в конечном итоге стать актуальным.

person Bastian    schedule 20.11.2010