我有一个包含大量结构的类型,我正在编写一个实用函数,它基本上在某些范围内作为reduce运行.每个结构将一组因变量与一个独立维度的某个点相关联 – 发明一个简化的例子,想象这会记录一段时间内房间的一系列环境条件:
// all examples are psuedocode for brevity struct TricorderReadings { float time; // independent variable float tempurature; float lightlevel; float windspeed; // etc for about twenty other kinds of data... }
我的函数只是执行cubic interpolation来猜测可用样本之间某些给定时间点的条件.
// performs Hermite interpolation between the four samples closest to given time float TempuratureAtTime( float time, sorted_vector<TricorderReadings> &data) { // assume all the proper bounds checking, etc. is in place int idx = FindClosestSampleBefore( time, data ); return CubicInterp( time, data[idx-1].time, data[idx-1].tempurature, data[idx+0].time, data[idx+0].tempurature, data[idx+1].time, data[idx+1].tempurature, data[idx+2].time, data[idx+2].tempurature ); }
我想概括一下这个功能,以便它可以一般地应用于任何成员,而不仅仅是温度.我可以想到三种方法来做到这一点,虽然它们都很容易编码,但我不确定从现在起一年内使用它的人最可读的是什么.这是我正在考虑的事情:
指向成员的语法
typedef int TricorderReadings::* selector; float ReadingAtTime( time, svec<TricorderReadings> &data, selector whichmember ) { int idx = FindClosestSampleBefore( time, data ); return CubicInterp( time, data[idx-1].time, data[idx-1].*whichmember, /* ...etc */ ); } // called like: ReadingAtTime( 12.6f, data, &TricorderReadings::windspeed );
这感觉就像最“C y”的方式,但它看起来很奇怪,整个指针到成员的语法很少使用,因此我的团队中的大多数人都很难理解.这是技术上“正确”的方式,但也是我将收到最混乱的电子邮件的方式.
结构偏移
float ReadingAtTime( time, svec<TricorderReadings> &data, int memberoffset ) { int idx = FindClosestSampleBefore( time, data ); return CubicInterp( time, data[idx-1].time, *(float *) ( ((char *)(&data[idx-1]))+memberoffset ), /* ...etc */ ); } // called like: ReadingAtTime( 12.6f, data, offsetof(TricorderReadings, windspeed) );
这在功能上与上面相同,但是指针数学是明确的.这种方法对于我的团队中的每个人(在C之前都学过C)都会立即熟悉和理解,并且它很强大,但它看起来很蹩脚.
模板化的Functor
template <class F> float ReadingAtTime( time, svec<TricorderReadings> &data ) { int idx = FindClosestSampleBefore( time, data ); return CubicInterp( time, data[idx-1].time, F::Get(data[idx-1]) ), /* ...etc */ ); } // called with: class WindSelector { inline static float Get(const TricorderReadings &d) { return d.windspeed; } } ReadingAtTime<WindSelector>( 12.6f, data );
这是最直接和STL-ish的做事方式,但它似乎是一大堆额外的输入和语法和即兴的类定义.它编译成几乎与上面两者完全相同的东西,但它也在可执行文件中转储了一堆冗余函数定义. (我已经用/FAcs对此进行了验证,但也许连接器会再次将它们取出来.)
以上三个都可以工作,编译器为所有这些代码发出几乎相同的代码;所以,我必须做出的最重要的选择就是最具可读性.你怎么看?
如果你的团队由相当聪明的人组成,我会说要信任他们和他们的能力,并使用指针到成员语法提供的技术上首选的解决方案.这是它的目的.如果你真的担心,你可以采取一些措施来缓解未来的麻烦
>在typedef附近的注释中注明并将其称为“指向成员的指针”语法,以便其他团队成员知道要查找的内容
>在代码审查中明确指出,其中许多应该存在.如果被认为难以理解或太难以维护,则提议更改它.
其他两种方法都存在问题,如您所描述的以及其他方面:
>两者都需要更多代码,有更多的拼写错误空间等.
> offsetof原语受限于可应用于哪种类型:
由于C语言中结构的扩展功能,在这种语言中,offsetof的使用仅限于“POD类型”,对于类,或多或少对应于结构的C概念(尽管非派生类只有公共非 – 虚拟成员函数,没有构造函数和/或析构函数也可以作为POD).
从here起.