只有当c++中有活动的东西时,才有一个额外的数据成员

Have an extra data member only when something is active in c++

本文关键字:有一个 数据成员 c++ 活动      更新时间:2023-10-16

我有一个队列的实现,类似于带有struct QueueItem { T data;}template <typename T> queue<T>,并且我还有一个单独的库,用于对数据在不同位置的传输进行计时(包括通过该队列从一个生产者线程到消费者线程)。为了做到这一点,我将该定时库中的代码插入到队列的推送和弹出函数中,这样当他们分配BufferItem.data时,他们也会将我添加的类型为void*的额外成员分配给该库中的一些定时元数据。也就是说,以前的

void push(T t)
{
  QueueItem i;
  i.data = t;
  //insert i into queue
}

成为

void push(T t)
{
  QueueItem i;
  i.data = t;
  void* fox = timinglib.getMetadata();
  i.timingInfo = fox;
  //insert i into queue
}

QueueItem来自

struct QueueItem
{
 T data;
}

struct QueueItem
{
  T data;
  void* timingInfo;
}

然而,我想实现的是,无论何时时序库未激活,都可以从后一个结构切换到更轻的结构。类似于:

if timingLib.isInactive()
 ;//use the smaller struct QueueItem
else
 ;//use the larger struct QueueItem

尽可能便宜。这样做的好方法是什么?

很明显,你不能同时拥有一个大小结构,所以你必须考虑某种形式的继承、指针/引用或并集。

如果T中有"空闲"数据可以被您的timingInfo占用,那么联合将是您的理想选择。如果没有,那么它将和原作一样"沉重"。

使用继承也可能和原来的一样大,因为它会在其中添加一个vtable,这会使它变得过于突出。

因此,下一个选项是只存储一个指针,并将该指针指向要存储的数据,无论是数据还是数据+计时。这种模式被称为"轻量级",即公共数据与被操作的对象分开存储。这可能就是你想要的(取决于时间信息元数据是什么)。

另一种更复杂的选择是有两个保持同步的队列。您将数据存储在一个中,另一个存储相关的计时信息(如果启用)。如果未启用,则忽略第二个队列。这样做的问题是确保2个保持同步,但这是一个组织问题,而不是技术挑战。也许可以创建一个新的Queue类,该类在内部包含两个真正的队列。

我将首先确认我的假设,即这需要是一个运行时选择,并且不能只构建两个启用/禁用定时的不同二进制文件。这种方法在任何方法中都尽可能多地消除了开销。

所以现在让我们假设我们想要不同的运行时行为。需要有运行时决策,因此有几个选项。如果你可以避免多态性的(相对较小的)成本,那么你可以让你的队列多态,并在启动时创建一个合适的实例,然后它的push会或不会添加额外的数据。

然而,如果这不是一个选项,我相信你可以使用模板来帮助完成你的目的,尽管可能会有一些前期工作,而且它可能会通过额外的代码增加你的二进制文件的大小。

您可以从一个模板开始为类添加计时:

template <typename Timee>
struct Timed : public Timee
{
    void* timingInfo;
};

然后一个定时的QueueItem看起来像:

Timed<QueueItem> timed_item;

对于任何不关心时间的东西来说,这个类看起来就像一个QueueItem:它会根据需要自动向上投射或切片到父级。如果一个方法需要知道定时信息,您可以创建一个重载来知道对Timed<T>做什么,或者进行运行时检查(针对"是否启用了定时"标志)并向下转换到正确的类型。

接下来,您需要更改Queue实例化,以了解它是使用基本QueueItem还是Timed版本。例如,一个可能机制的非常粗略的草图:

template <typename Element>
void run()
{
    Queue<Element> queue;
    queue.setup();
    queue.process();
}
int main()
{
    if(do_timing)
    {
        run<Timed<QueueItem> >();
    }
    else
    {
        run<QueueItem>();
    }
    return 0;
}

当与Timed项一起使用时,您"可能"需要对Queue进行专门化,除非获取元数据是无状态的,在这种情况下,Timed构造函数可以收集信息并在创建时自行填充。那么Queue就保持不变,并依赖于您正在使用的实例化。

相关文章: