Генерация структур данных путем разбора текстовых файлов - PullRequest
7 голосов
/ 28 апреля 2009

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

class Character
{
public:
    int x, y; // Character's location
    Character* teammate;
}

Я настроил свой парсер для чтения из файла структуры данных с синтаксисом, похожим на C ++

Character Sidekick
{
    X = 12
    Y = 0
}

Character AwesomeDude
{
    X = 10
    Y = 50
    Teammate = Sidekick
}

Это создаст две структуры данных и поместит их в карту <std::string, Character*>, где ключевой строкой будет любое имя, которое я ей дал (в данном случае Sidekick и AwesomeDude). Когда мой анализатор видит указатель на класс, такой как указатель товарища по команде, он достаточно умен, чтобы посмотреть на карте, чтобы получить указатель на эту структуру данных. Проблема в том, что я не могу объявить товарища по команде Sidekick AwesomeDude, потому что он еще не помещен в карту персонажей.

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

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

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

Ответы [ 6 ]

13 голосов
/ 28 апреля 2009

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

Когда файл будет создан, просмотрите те, у которых есть ссылки, и разрешите их.

5 голосов
/ 29 апреля 2009

Ну, вы попросили третий вариант. Вам не нужно использовать XML, но если вы следуете следующей структуре, было бы очень просто использовать SAX-парсер для построения вашей структуры данных.

В любом случае, вместо ссылки на товарища по команде, каждый персонаж ссылается на команду (синяя команда в данном случае). Это отделит круговую ссылку. Просто убедитесь, что вы перечислили команды перед персонажами.

<team>Blue</team>

<character>
    <name>Sidekick</name>
    <X>12</X>
    <Y>0</Y>
    <teamref>Blue</teamref>
</character>

<character>
    <name>Sidekick</name>
    <X>10</X>
    <Y>50</Y>
    <teamref>Blue</teamref>
</character>
2 голосов
/ 29 апреля 2009

Лично я бы пошел с б). Разделение вашего кода на классы Parser и Validator, которые работают с одной и той же структурой данных. Парсер будет читать и анализировать файл, заполняя структуру данных и сохраняя любые ссылки на объекты как их текстовые имена, оставляя пока настоящий указатель нулевым в вашей структуре.

Когда вы закончите загрузку файлов, используйте класс Validator для проверки и разрешения любых ссылок, заполняя «настоящие» указатели. Возможно, вы захотите подумать о том, как структурировать ваши данные, чтобы сделать этот поиск красивым и быстрым.

1 голос
/ 29 апреля 2009

Уилл сказал именно то, что я собирался написать. Просто держите список или что-то с нерешенными ссылками.

И не забудьте выдать ошибку, если есть неразрешенные ссылки, когда вы закончите читать файл = P

0 голосов
/ 29 апреля 2009

Один из вариантов - отменить обязательство. Карта отвечает за заполнение справки

template<T> class SymbolMap // I never could rememeber C++ template syntax
{
   ...

   /// fill in target with thing name
   /// if no name yet, add it to the list of thing that will be name
   void Set(T& target, std::string name);

   /// define name as target
   /// go back and fill in anything that needs to be name
   void Define(T target, std::string name);

   /// make sure everything is resolved
   ~SymbolMap()
}

это не будет хорошо взаимодействовать с семантикой значения / перемещения, но я подозреваю, что не так уж много.

0 голосов
/ 29 апреля 2009

Вместо хранения объекта персонажа на карте, сохраните прокси для персонажа. Прокси будет содержать указатель на фактический объект Character, когда объект загружен. Тип Character :: teammate будет изменен на этот тип прокси. Когда вы читаете ссылку, которой нет на вашей карте, вы создаете прокси и используете прокси. Когда вы загружаете персонажа, у которого уже есть пустой прокси на карте, заполните его новым загруженным персонажем. Вы также можете добавить счетчик, чтобы отслеживать количество пустых прокси на карте, чтобы вы знали, когда все ссылочные символы были загружены.

Еще один слой косвенности ... он всегда делает программирование легче и медленнее.

...