在包含函数调用的范围内持续的临时函数参数

时间:2013-04-25 15:08:02

标签: c++

考虑一个填充缓冲区的典型函数:

const char* fillMyBuffer( const char* buf, int size );

假设这个函数用一些有用的数据填充缓冲区,我想在调用后几乎立即使用,然后我想摆脱缓冲区。

这样做的有效方法是在堆栈上分配:

doStuff();

{
    char myBuf[BUF_LEN];
    const char* pBuf = fillMyBuffer( myBuf, BUF_LEN );

    processBuffer( pBuf );
}

doOtherStuff();

所以这对我的库很有用,因为缓冲区是在堆栈上分配的 - 基本上没有分配,使用和丢弃的成本。它持续包含大括号的整个范围。

但我有一个图书馆,我一直都在做这个模式。我想稍微自动化一下。理想情况下,我希望代码看起来像这样:

doStuff();

{
    // tricky - the returned buffer lasts the entire scope of the braces.
    const char* pBuf = fillMyBufferLocal();

    processBuffer( pBuf );
}

doOtherStuff();

但是如何实现这个目标?

我做了以下操作,这似乎有效,但我知道这与标准相反:

class localBuf
{
public:
    operator char* () { return &mBuf[0]; }
    char mBuf[BUF_LEN];   
};

#define fillMyBufferLocal()   fillMyBuffer( localBuf(), BUF_LEN );

实际上,缓冲器在包含支架的整个寿命期间在堆叠上持久。但是标准说该对象只需要持续到函数返回。例如。从技术上讲,它就像我在函数内部的堆栈上分配缓冲区一样不安全。

有没有一种安全的方法来实现这一目标?

3 个答案:

答案 0 :(得分:3)

我一般会推荐您的原始解决方案。它将缓冲区的分配与填充分开。但是,如果要实现此fillMyBufferLocal替代方法,则必须动态分配缓冲区并返回指向它的指针。当然,如果你将一个原始指针返回到动态分配的内存,那么很明显以后应该销毁内存。相反,返回一个封装相应所有权的智能指针:

std::unique_ptr<char[]> fillMyBufferLocal()
{
  std::unique_ptr<char[]> buffer(new char[BUF_LEN]);
  // Fill it
  return buffer;
}

然后你可以像这样使用它:

auto buffer = fillMyBufferLocal();
processBuffer(buffer.get());

答案 1 :(得分:2)

我认为你不应该这样做。它只是让代码更难理解。

自动存储持续时间意味着当对象超出范围时,它将被销毁。在这里,您希望将系统欺骗为行为,就像创建具有自动存储持续时间的对象(即在堆栈上分配),但不遵守相应的规则(即从{{1返回时不被破坏) }})。

在我看来,你可以做的最接近,最有意义的事情是使用fillMyBuffer()可以重用的全局缓冲区,或者让缓冲区成为fillMyBuffer()内的static变量。例如:

fillMyBuffer()

但是,我强烈建议您重新考虑您的要求,并且:

  • 继续使用您当前采用的解决方案(即在堆栈上透明分配);或
  • template<int BUF_LEN = 255> const char* fill_my_buffer() { static char myBuf[BUF_LEN]; // Fill... return myBuf; } 内动态分配缓冲区,并将RAII包装器(如fillMyBuffer())返回到此动态分配的缓冲区。

<强>更新

作为最后的绝望尝试,您可以定义一个宏来为您分配和调用unique_ptr

fill_my_buffer()

然后你会这样使用它:

#define PREPARE_BUFFER(B, S) \
    char buffer[S]; \
    const char* B = fill_my_buffer(buffer, S);

答案 2 :(得分:1)

您可以编写一个包含基于堆栈的缓冲区的类,并转换为char const *,例如

void processBuffer(char const * buffer);
char const * fillMyBuffer(char const * buffer, int size);
int const BUF_LEN = 123;

class Wrapper
{
public:
  Wrapper(char const * (*fill)(char const *, int))
  {
    fill(&m_buffer[0], m_buffer.size());
  }

  operator char const * () const { return &m_buffer[0]; }

private:
  std::array<char, BUF_LEN> m_buffer;
};


void foo()
{
  Wrapper wrapper(fillMyBuffer);

  processBuffer(wrapper);

}