By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
454,605 Members | 1,391 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 454,605 IT Pros & Developers. It's quick & easy.

Array CopyConstruct as efficiently as possible

P: n/a

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...

--

Frederick Gotham
Nov 15 '06 #1
Share this Question
Share on Google+
1 Reply


P: n/a
Frederick Gotham wrote:
>
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)
Nov 15 '06 #2

This discussion thread is closed

Replies have been disabled for this discussion.