对布景问题

爱丽丝:为什么这个地方很奇怪?
渡渡鸟:因为其他所有地方都不是很奇怪。 必须至少有一个非常奇怪的地方。



因此,我们将考虑模板类BitSet的文本,以使其适应MK的要求,优化的主要领域已在前面确定。 当然,您可以从头开始编写自己的类,但是我们不要忽略获得良好解决方案的机会,因为STL库(不要与spl混淆)已经为人所知很长时间,并且已在各处使用。 首先,您需要找到源代码,在Internet上短暂旅行之后,我只是用MinGW打开目录并查找所需的文件,我打算对此进行进一步讨论。

最初,我们看到的页面不只是包含版权和许可证文本以及各种警告的页面-嗯,您可以跳过此步骤,这是给律师的。 接下来是其中的内容,我要感谢作者-每个人都附有关于我们希望从每个文件中获取的内容的评论-每个人都可以这样做, 印第安纳琼斯研究员的工作会更容易。 接下来是定义,我对代码的清洁度并不热衷,也不会争论-顺其自然,马上就可以看到存储单元中位数的定义,这是一个无符号的long。 继续沿着Defines的湿滑路径,我正在寻找自己的存储单元定义,该定义等于uint8_fast,尽管我知道显然这还不够。 顺便说一句,我立即感谢作者的风格-在他们自己清理后的模块末尾,破坏了内部定义-我什至不希望我在现代生产中看到这样的东西。

从这里开始就产生了一些误解-首先,他们定义模板辅助结构struct _Base_bitset(为什么是结构而不是类,因为它们是可互换的),因此它们确定数据存储的类型并再次直接指定-我们将其更改为自己的类型。 接下来,确定此辅助结构的两个实例-对于一个存储单元(这是可以理解的,以提高代码的效率)和对于完全不存储的简并实例(可能用于错误处理),直到我们触摸此代码为止。

接下来,我们找到从模板结构派生的主类类位集(现在是该类)(是的,在C中可能尚不清楚为什么这样做,但有可能),其中存储类型被再次确定并再次更改为我们的存储类型。 但是在这里,我想知道-为什么类型没有继承自父结构,却惊奇地发现(是的,模板类,这不是我在日常生活中所做的事)模板类的继承与普通类有所不同。 也就是说,继承是完全相同的,父级的所有实体都可以在子级(我希望没有人接受我的性别沙文主义)中使用,但是必须用全名表示。 嗯,这仍然可以理解,否则编译器将不会猜测要应用哪个实例化选项,但是如果您使用类中定义的类型,则仍然需要添加typename关键字。 这不是一个显而易见的解决方案,我对编译器的诊断感到特别满意,这意味着“也许您有父类的类型”-是的,谢谢队长,这就是我的初衷,很高兴我们彼此了解,但是对我来说并没有变得容易。 但是,这样的诊断是在旧版本的GCC中进行的,在当前消息中,它变得更容易理解“您可能已经忘记了typename关键字”。 理解编译器对模板类的诊断消息通常是另外一首歌的主题,而这首歌绝不是一件快乐的事。

因此,如果我们不想在子类中不断使用该构造

std::_Base_bitset<Nb>::_WordT 

(我们不想要,否吗?)那么我们有三种方法

1)琐碎的

 #define _WordT typename _Base_bitset<_Nb>::_WordT 

2)很明显-在全局级别上的类型定义,我更喜欢

3)对于陌生的粉丝

 typedef typename _Base_bitset<_Nb>::_WordT _WordT; 

(宏伟的镀铬拐杖的持久感觉)。 就个人而言,在所有三种情况下,我都不会对父类的直接指示感到满意,因为实现不应考虑继承链,但也许我不了解。

还有一种方法4)

  using _WordT = std::_Base_bitset<Nb>::_WordT; 

但在我的编译器版本中,它不起作用,因此无效。

关于页边空白(PNP)的注意事项-精彩的书“面向真正程序员的C ++”(当时我错误地下载了该书,并决定将其专门用于实时系统中的C ++),其中提到了语言“它的优雅绝不在于简单性(C ++单词和简单性因其明显的矛盾而割耳),但具有潜在的功能。 对于每一个丑陋的问题,都有一种聪明的成语,一种优雅的语言fe讽,这使问题在我们眼前融化了。” 既然这本书真的很棒,那意味着上面的引用是正确的(我知道这里有一定的逻辑延伸),但是就个人而言,不幸的是,我看到了问题,但是有一个聪明的成语。

尽管仍然感到有些困惑,但问题仍然可以解决,您可以享受结果-但是还没有。 将测试集的大小从15调整为5时,完全意外发生了编译错误。 不,一切都清楚了,使用模板参数1的未修改实例化是可行的,但是从外面看,它看起来很奇怪-我们更改了常数,程序停止了编译。

当然,您可以修改此实现以及其他实现,但是这种方法看起来完全违反了DRY原理。 有可能的解决方案,其中不止一种:1)显而易见的-再次是define,2)琐碎的-再次是全局级别的类型定义,但是在这种情况下,它将超出模块范围,但是,在考虑中的实现中确实存在,但超出了基本结构,但也无需编写限定词。

因此,我倾向于选择3)基类,用于确定类型以及所有实例从该类的继承。 而且,父类的实体再次受到保护,因此它们不会伸出。 然后我发现一个有趣的属性,也许C ++的灵活性值得称赞-对于没有存储的变体,不需要类型,并且该语言允许在不继承的情况下使用实现,尽管在这种情况下这不是特别必要。 我立即发现该库的另一个缺点-在所有三种情况下,每次都会设置用于计算存储单元数的操作

 static size_t _S_whichword 

和其中的位掩码_S_maskbit完全相同-我们还将它们移到基类。 在这种情况下,检测到“死”代码-_S_whichbyte方法,我什至不知道该怎么做-一方面,好的音调规则要求将其删除;对于模板,这不会影响生成的代码。 我将使用规则-“不了解某些内容-请勿触摸”并保留此方法。

原则上,关于存储类型的修改已完成,您可以开始测试。 立即我发现缺乏实现-对于MSP430架构,出于某种原因,分配了两个字节的字而不是字节。 当然,不是像以前那样使用双字,但我们仍在争取最少的代码(在每种意义上)。 事实证明,尽管命令系统具有字节操作且编译器本身已完全使用它们,但编译器确定在此体系结构中uint_fast8_t类型的大小为2。 使用这种类型的想法受到了损害,您必须直接设置uint8_t数据类型。 好吧,如果存在无法成功处理字节的体系结构,并且uint_fast8_t类型的大小不同于1(编译器中没有可用的大小),那么它将不得不为其他所有速度而苦恼。

测试校正后的版本可以显示其在各种体系结构上和具有不同参数的正确操作,但是仍然存在计算MK的位掩码而不产生偏移的问题,在我们的情况下是MSP430和AVR。 原则上,无论MK架构如何,您都可以简单地使从阵列读取位掩码成为所有情况下的一种方法。 该解决方案非常有效,在已建立索引的发达架构中,一切都很好,但是与快速转换相比,仍然会浪费时间,但是我不想用“班级会更快,他说,我们正在优化他说。

因此,对于两个弱体系结构,我们需要不同的实现,它们在uint_fast16_t类型的大小方面与所有其他弱体系结构不同-它是2对4,对于64位版本是8。 有条件的编译对我们没有帮助,但是为优化而设置条件并不是武士的方式,剩下的就是模式。 我正在尝试创建该类的模板方法,但是无法使用部分专业化-事实证明应该如此,甚至找到一篇文章说明为什么这是一个好的解决方案。 老实说,我仍然不明白为什么这个决定是好的,很可能是出于宗教考虑。 尽管如此,我们并没有被问到,还有什么要做的-您可以创建一个友好的模板函数(事实证明这是不可能的,并且出于同样的原因-禁止部分专业化),还有另一种解决方案,它看起来很有趣-该方法在类主体之外进行了部分专业化。 您甚至可以创建一个单独的模板函数,并从类方法中访问它,我不知道哪种方法更正确,我选择了这个方法。 是的,她无权访问班级的内部实体,但是在这种特殊情况下,这是没有必要的,如果有必要该怎么办,我还不知道,“我们将在问题出现时解决问题”。

现在我们有了所需的一切,我们将经过测试的片段收集在一起,并获得了解决初始优化问题的代码。 同时,我们使代码更紧凑(因此更易于理解,尽管后者值得商)),消除了大量重复并消除了多余的实体。 唯一不固定的是结构和类的混合,但是在这里我不了解执行此实现的原因,因此,以防万一,我不会涉及到这一部分。

第二篇文章将专门介绍该版本的第二版的实现,如果没有第二篇,将会有很多成果。

Source: https://habr.com/ru/post/zh-CN450176/


All Articles