开发者

Optimal way to fill std::vector<char> buffer

This buffer should contain slots (three in this example) of equal length ( 20 in this example)

The buffer has to have contiguous memory so that it can be passed to a C function in non-const fashion.

const int slot_size = 20;
std::vector<char> vbuffer;

This function takes a string, copies to a temporary buffer of the require开发者_开发问答d size then appeds it to vbuffer

void prepBuffer( const std::string& s)
{
  std::vector<char> temp(slot_size);
  std::copy(s.c_str(), s.c_str() + s.length() + 1, temp.begin());
  vbuffer.insert(vbuffer.end(), temp.begin(), temp.end());
}

Testing the function

int main()
{
  vbuffer.reserve(60);
  prepBuffer( "Argentina");
  prepBuffer( "Herzegovina");
  prepBuffer( "Zambia");

  cout << &vbuffer[0] << endl;
  cout << &vbuffer[20] << endl;
  cout << &vbuffer[40] << endl;
}

Question. There is a lot of string copying in my prepBuffer function. I am looking for a better way to fill up vbuffer with minimal copying

EDIT

The size of slots is determined elsewhere in the program. But it is not known at compile time.

EDIT

In line with my accepted answer below, I have settled on this version

void prepBuffer(const std::string& s)
{
  assert(s.size() < slot_size );
  vbuffer.insert(vbuffer.end(), s.begin(), s.end());
  vbuffer.insert(vbuffer.end(), slot_size - s.size(), '\0' ); 
}

Suggestions are still welcome


How about this:

vbuffer.reserve(vbuffer.size() + 20);
vbuffer.insert(vbuffer.end(), s.begin(), s.end());
vbuffer.insert(vbuffer.end(), 20 - s.size(), '\0');

An additional check on the string length is recommended, along with a policy for handling over-long strings (e.g. assert(s.size() < 20);).


If you don't use std::string at all and avoid the temporary std::vector, you can easily do this without any extra dynamic allocation.

template <unsigned N>
void prepBuffer(char const (&s)[N])
{
    std::copy(s, s + N, std::back_inserter(vbuffer));
    vbuffer.resize(vbuffer.size() - N + 20);
}

Or, since the number of characters to be written is known ahead of time, you could just as easily use a nontemplate function:

void prepBuffer(char const* s)
{
    unsigned n = vbuffer.size();
    vbuffer.resize(n + 20);
    while (*s && n != vbuffer.size())
    {
        vbuffer[n] = *s;
        ++n;
        ++s;
    }

    assert(*s == 0 && n != vbuffer.size());
    // Alternatively, throw an exception or handle the error some other way
}


Another idea:

std::vector<std::array<char, 20> > prepped(3);

strncpy(prepped[0].begin(), "Argentina",   20);
strncpy(prepped[1].begin(), "Herzegovina", 20);
strncpy(prepped[2].begin(), "Zambia",      20);

You could write

typedef std::vector<std::array<char, 20> > prepped_t;
strncpy(..., ..., sizeof(prepped_t::value_type));

in case you wanted to be a bit more flexible when changing the size of the nested array


void prepBuffer( const char *s, std::size_t offset)
{
  strncpy(&vbuffer[offset], s, 20);
}

Testing the function

int main()
{
  vbuffer.resize(60);
  prepBuffer( "Argentina", 0);
  prepBuffer( "Herzegovina", 20);
  prepBuffer( "Zambia", 40);

  cout << &vbuffer[0] << endl;
  cout << &vbuffer[20] << endl;
  cout << &vbuffer[40] << endl;
}

That minimizes copying, at the cost of maintainability.


Here is nearly-optimal code that is still readable and maintainable.

std::string vbuffer;
void prepBuffer( const std::string& s)
{
  vbuffer += s;
  vbuffer.resize( ( (vbuffer.size() +19) / 20) * 20));
}

Testing the function

int main()
{
  vbuffer.reserve(60);
  prepBuffer( "Argentina");
  prepBuffer( "Herzegovina");
  prepBuffer( "Zambia");

  cout << &vbuffer[0] << endl;
  cout << &vbuffer[20] << endl;
  cout << &vbuffer[40] << endl;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜