views:

87

answers:

2
template<typename OutputIterator>
void BlitSurface::ExtractFrames(OutputIterator it,
                                int frame_width, int frame_height,
                                int frames_per_row, int frames_per_column,
                                bool padding) const
{
    SDL_Surface ** temp_surf = SDL_Ex_ExtractFrames(_surface, frame_width, frame_height, frames_per_row, frames_per_column, padding);

    int surface_count = frames_per_row * frames_per_column;

    for(int i=0; i<surface_count; ++i)
    {
        BlitSurface bs;
        bs._surface = temp_surf[i];
        *it = bs;
        ++it;
    }

    delete [] temp_surf;
}

I have this function, which works fine. Only problem is that I don't want to invoke the copy constructor, because it copies the entire surface, and I only need to copy the pointer. I just want to use the default constructor, then set the member _surface to temp_surface[i], like this:

for(int i=0; i<surface_count; ++i)
{
    it->_surface = temp_surf[i];
    ++it;
}

That works for normal iterators, but not for insertion iterators. How can I fix it to work for both?

A: 

It's mentioned that a copy constructor is being called. In the example that provided it seems like the container is probably defined to hold BlitSurface. Something like std::vector< BlitSurface>. This is a guess on my part from the following lines:

    BlitSurface bs;
    bs._surface = temp_surf[i];
    *it = bs;

My understanding is that all std containers will make a copy upon insert. From there on you can use the objects in the container by reference. If you do not want the copy constructor to be called on BlitSurface then I would suggest that the container store a pointer to BlitSurface. This way when the container does its copy on insert the object it actually makes a copy of is a pointer (not the BlitSurface object that is pointed to).

    BlitSurface* bs = new BlitSurface;
    bs->_surface = temp_surf[i];
    *it = bs;

Keep in mind that that this approach allocates on heap (i.e. new) so memory will have to be explicitly deleted later or some type of smart pointer can be used in the container to take care of the deletion (std::vector< boost::shared_ptr< BlitSurface> > ).

skimobear
+1  A: 

Really what you want is a move InputIterator for use with the insertion OutputIterator. Since that doesn't exist in C++03, there needs to be an alternative way to signal that a "shallow" move, not a "deep" copy, is desired.

A simple state flag in the object itself won't work, because the implementation is allowed to copy the object around randomly before actually putting it in the container. (For optimization's sake, you know it won't, but it's nice not to worry about debug builds.)

Off the top of my head, it sounds like a job for a custom allocator. The default allocator copy-constructs using placement new; you can define an alternate constructor and call it using placement new instead.

template< typename T >
struct move_traits {
    typedef T must_copy_type; // does not exist in specializations
};

template< typename T >
struct move_if_possible_allocator
    : std::allocator< T > {
    typedef move_traits<T> traits;

        // SFINAE selects this function if there is a specialization
    void construct( typename traits::may_move_type *obj, T &value ) {
        new( obj ) T(); // default construct
        traits::move_obj( *obj, value ); // custom routine
    }

        // SFINAE selects this function if traits is the base template
    void construct( typename traits::must_copy_type *obj, T const &value ) {
        new( obj ) T( value ); // copy construct (fallback case)
    }

    // define rebind... exercise for the reader ;v)
};

template<>
struct move_traits< BlitSurface > {
    typedef T may_move_type; // signal existence of specialization
    static void move_obj( BlitSurface &out, BlitSurface &in ) {
        // fill out and clear in
    }
}

Of course, it's perfectly fine to add state to BlitSurface to disable moving by move_obj, if some objects are in fact copied into the container.

Potatoswatter
Your code is Greek to me, but I understand your explanation. I've avoided dealing with allocators up to this point, I guess it's time to learn about them. I won't be able to implement this effectively now, but you've pointed me in the right direction. Thanks.
PigBen