Array CopyConstruct as efficiently as possible

F

Frederick Gotham

If we want to copy an array of POD's, we can simply do:

SomePODType src[8] = { ... }, dest[8];

memcpy(&dest,&src,sizeof dest);

We can assume that this method is definitely faster than (if not at least
as fast as) the following method:

T *p = dest;
T const *const pover = dest + sizeof dest;

T const *q = src;

do *p++ = *q++;
while (pover != p);

This method won't work for class types, because the constructors and
assignment operators may acquire resources and so forth.

The following is a basic attempt to implement a universal method of copy-
constructing an array:

#include <cstddef>
#include <new>

template<class T,std::size_t len>
void CopyCstr(T const (&src)[len],void *const dest)
{
T *p = (T*)dest;
T const *const pover = p + len;

T const *q = src;

do ::new((void*)p++) T(*q++);
while (pover != p);
}


This looks grand, but what happens if we use it to copy-construct an array
of short ints? It will look like as follows:

void CopyCstr(short const (&src)[8],void *const dest)
{
short *p = (short*)dest;
short const *const pover = p + 8;

short const *q = src;

do ::new((void*)p++) short(*q++);
while (pover != p);
}

The only problem with this is that it may not be as efficient as it could
be. We'd be better off with simply:

void CopyCstr(short const (&src)[8],void *const dest)
{
memcpy(dest,src,sizeof src);
}

So I wonder how we can achieve the best of both worlds with the one sole
template function? If we had a way of knowing that the "normal
initialisation" for a particular type was a no-op, then we could take
advantage of it. Something like

template<class T,std::size_t len>
void CopyCstr(T const (&src)[len],void *const dest)
{
if ( NoOp:):new(void*) T) ) memcpy(dest,src,sizeof src);
else
{
T *p = (T*)dest;
T const *const pover = p + len;

T const *q = src;

do ::new((void*)p++) T(*q++);
while (pover != p);
}

Obviously, the "if" conditional would be known at compile-time to be either
true or false, the the opposite command path could be done away with.

Anyway, this was just a thought that went through me head...
 
P

Pete Becker

Frederick said:
So I wonder how we can achieve the best of both worlds with the one sole
template function? If we had a way of knowing that the "normal
initialisation" for a particular type was a no-op, then we could take
advantage of it.

std::tr1::has_trivial_copy gives you that information.

// sketch, untested:

template <class Ty, bool>
struct copier
{ /* element by element copy */
static void init(Ty *tgt,
const Ty *src, unsigned count);
};

template <class Ty>
struct copier<Ty, true>
{ /* byte copy */
static void init(Ty *tgt, const Ty *src, unsigned count);
};

void init(Ty *tgt, const Ty *src, unsigned count)
{
copier<Ty, has_trivial_copy<Ty>::value> >::
init(tgt, src, count);
}

For a complete example (using assignment, not construction), see listing
8 in the section "Type Traits" in my article at
http://www.ddj.com/dept/cpp/184401964 (limited access, unfortunately).

--

-- Pete
Roundhouse Consulting, Ltd. (www.versatilecoding.com)
Author of "The Standard C++ Library Extensions: a Tutorial and
Reference." (www.petebecker.com/tr1book)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Staff online

Members online

Forum statistics

Threads
473,756
Messages
2,569,535
Members
45,008
Latest member
obedient dusk

Latest Threads

Top