当前位置 : 主页 > 编程语言 > c++ >

在C中编写structarray map functor的最简单方法

来源:互联网 收集:自由互联 发布时间:2021-06-23
这是对最可读方式的意见的调查 – 无论是使用C指针到成员,字节偏移还是模板化仿函数来定义“从结构foo中选择成员X”. 我有一个包含大量结构的类型,我正在编写一个实用函数,它基本
这是对最可读方式的意见的调查 – 无论是使用C指针到成员,字节偏移还是模板化仿函数来定义“从结构foo中选择成员X”.

我有一个包含大量结构的类型,我正在编写一个实用函数,它基本上在某些范围内作为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起.

网友评论