我为我正在编写的游戏编写了一个文件解析器,以便我自己更改游戏的各个方面(比如角色/舞台/碰撞数据)。例如,我可能有这样的角色类:
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)让解析器读取文件两次,一次用指针填充空数据结构,第二次到经过并填写。
(a)的问题在于我也可以决定在类上调用哪个构造函数,如果我转发声明的东西,我必须让构造函数与其余数据分开,这可能会令人困惑。 (b)的问题是我可能想在自己的文件中声明Sidekick和AwesomeDude。我必须让我的解析器能够读取一个文件列表而不是一次只读一个(我猜这不是很糟糕,虽然有时候我可能想得到一个文件列表来读取文件)。 (b)还有一个缺点,即无法使用构造函数本身后面声明的数据结构,但我认为这不是一个大问题。
哪种方式听起来更好?有没有第三种选择我没想过?似乎应该有一些聪明的解决方案,使用指针引用或绑定或其他......: - /我认为这有点主观,基于我想给自己的功能,但任何输入都是受欢迎的。
答案 0 :(得分:13)
第一次遇到引用时,只需将其存储为引用即可。然后,您可以将字符,引用或其他内容放在“以后需要解析的引用”列表中。
文件完成后,请浏览那些有引用并解决它们的文件。
答案 1 :(得分:5)
嗯,你问了第三种选择。您不必使用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 :(得分:2)
就个人而言,我会选择b)。将代码拆分为Parser和Validator类,两者都在相同的数据结构上运行。解析器将读取并解析文件,填充数据结构并将任何对象引用存储为其文本名称,暂时将实际指针留在结构中。
完成加载文件后,使用Validator类验证和解析任何引用,填写“真实”指针。您将需要考虑如何构建数据以使这些查找更加快速。
答案 3 :(得分:1)
我会准确地说出我要写的内容。只需保留列表或其他未解决的参考文献。
在读完文件= P
后,如果有未解决的引用,请不要忘记抛出错误答案 4 :(得分:0)
不是在地图中存储Character对象,而是存储Character的代理。代理将在加载对象时包含指向实际Character对象的指针。 Character :: teammate的类型将更改为此代理类型。当您读入地图中尚未存在的引用时,您将创建代理并使用代理。当您在地图中加载已有空代理的字符时,请使用新加载的字符填充该字符。您可能还想添加一个计数器来跟踪您在地图中有多少空代理,以便知道何时加载了所有引用的字符。
另一层间接......它总是让编程变得更容易和更慢。
答案 5 :(得分:0)
一种选择是扭转义务。地图负责填写参考资料
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()
}
与价值/移动语义不能很好地互动,但我怀疑不会那么多。