On Wed, 2016-08-10 at 21:31 -0400, Trevor Saunders wrote:

> 
> Well, I'd say the compromises made in std::string make it pretty
> terrible for general purpose use accept where perf and memory doesn't
> matter at all.
> 
> std::vector isn't very great size wise either, imho the size / 
> capacity fields would be much better put in the buffer than in the 
> struct itself, to save 2 * sizeof (void *) in sizeof std::vector.

There's nothing in the standard that says those fields have to go into
the vector struct/class itself and not into the data buffer.  It just
happens to be the way it's implemented in libstdc++.  E.g. libc++
implements some things in different ways to save a few bytes here and
there.

It looks more practical to put those fields into the container itself,
otherwise you'd have to do a nullptr check every time you access the
size field etc.  Although newer compilers optimize away the redundant
nullptr checks, older compilers (which GCC wants to be good with) might
not do it.  In any case, it seems to generate slightly bigger code, at
least in one instance (see attachment).

Anyway, normally there is more data in the vectors than there are
vectors around, so whether sizeof (vector) is 8 or 24 bytes doesn't
matter.

> 
> or just having your stack_string type convert to the normal string 
> type so that you can pass mutable references.

But that would imply copying, wouldn't it?

Cheers,
Oleg
#if 0
template <typename T>
class vector
{
public:
  unsigned int size (void) const { return m_buffer != nullptr ? m_buffer->size : 0; }
  bool empty (void) const { return size () == 0; }

  unsigned int capacity (void) const { return m_buffer != nullptr ? m_buffer->capacity : 0; }

  T* data (void) { return (T*)(m_buffer + 1); }
  const T* data (void) const { return (const T*)(m_buffer + 1); }

  T& operator [] (unsigned int i) { return data ()[i]; }
  const T& operator [] (unsigned int i) const { return data ()[i]; }

private:
  struct buffer_header
  {
    unsigned int size;
    unsigned int capacity;
  };

  buffer_header* m_buffer;
};


int foo (const vector<int>& x)
{
  if (x.empty ())
    return 0;

  int r = 0;
  for (unsigned int i = 0; i < x.size (); ++i)
    r += x[i];

  return r;
}

/*
-O2 -m2a
	mov.l	@r4,r2
	tst	r2,r2
	bt	.L5
	mov.l	@r2,r1
	tst	r1,r1
	bt.s	.L5
	shll2	r1
	add	#-4,r1
	shlr2	r1
	add	#8,r2
	mov	#0,r0
	add	#1,r1
	.align 2
.L3:
	mov.l	@r2+,r3
	dt	r1
	bf.s	.L3
	add	r3,r0
	rts/n
	.align 1
.L5:
	rts
	mov	#0,r0
*/
#endif


#if 1

template <typename T>
class vector
{
public:
  unsigned int size (void) const { return m_size; }
  bool empty (void) const { return size () == 0; }

  unsigned int capacity (void) const { return m_capacity; }

  T* data (void) { return (T*)(m_buffer); }
  const T* data (void) const { return (const T*)(m_buffer); }

  T& operator [] (unsigned int i) { return data ()[i]; }
  const T& operator [] (unsigned int i) const { return data ()[i]; }

private:
  unsigned int m_size;
  unsigned int m_capacity;
  T* m_buffer;
};


int foo (const vector<int>& x)
{
  if (x.empty ())
    return 0;

  int r = 0;
  for (unsigned int i = 0; i < x.size (); ++i)
    r += x[i];

  return r;
}

/*
-O2 -m2a
	mov.l	@r4,r1
	tst	r1,r1
	bt.s	.L7
	mov	#0,r0
	shll2	r1
	mov.l	@(8,r4),r2
	add	#-4,r1
	shlr2	r1
	add	#1,r1
	.align 2
.L3:
	mov.l	@r2+,r3
	dt	r1
	bf.s	.L3
	add	r3,r0
.L7:
	rts/n
*/

#endif

Reply via email to