Re: A deque containing different types of objects (with a common base class)
Juha Nieminen wrote:
Kai-Uwe Bux wrote:
Here is a proof of concept:
Basically you are allocating each element separately with new?
That's exactly what I wanted to avoid in the first place.
Sure, but your main argument against it was that using new and delete is
error prone. If you hide that away from the user, that objection
disappears. The efficiency concern is misplaced without profiling showing a
need for optimization.
I was simply asking if the idea could work.
a) You are engaged in premature optimization.
b) The proof of concept can be amended as follows:
#include <vector>
#include <algorithm>
#include <iterator>
#include <new>
template < typename T, typename AllignedPod >
class polymorphic_var {
typedef void (* copy_fct ) ( AllignedPod const &, AllignedPod & );
typedef T & (* ref_fct )( AllignedPod & );
typedef void ( *destr_fct ) ( AllignedPod & );
template < typename D >
static
void copy_construct ( AllignedPod const & from, AllignedPod & to ) {
new ( (void*)&to ) D ( reinterpret_cast<D const &>(from) );
}
template < typename D >
static
T & get_reference ( AllignedPod & d ) {
return ( reinterpret_cast< D& >( d ) );
}
template < typename D >
static
void destroy ( AllignedPod & d ) {
reinterpret_cast<D*>( &d )->~D();
}
AllignedPod the_data;
copy_fct the_copy_fct;
ref_fct the_ref_fct;
destr_fct the_destr_fct;
public:
polymorphic_var ( T const & t = T() )
: the_data ()
, the_copy_fct ( ©_construct<T> )
, the_ref_fct ( &get_reference<T> )
, the_destr_fct ( &destroy<T> )
{
the_copy_fct( reinterpret_cast<AllignedPod const &>(t), the_data );
}
polymorphic_var ( polymorphic_var const & other )
: the_data ()
, the_copy_fct ( other.the_copy_fct )
, the_ref_fct ( other.the_ref_fct )
, the_destr_fct ( other.the_destr_fct )
{
the_copy_fct( other.the_data, the_data );
}
template < typename D >
polymorphic_var ( D const & d )
: the_data ()
, the_copy_fct ( ©_construct<D> )
, the_ref_fct ( &get_reference<D> )
, the_destr_fct ( &destroy<D> )
{
the_copy_fct( reinterpret_cast<AllignedPod const &>(d), the_data );
}
polymorphic_var operator= ( polymorphic_var rhs ) {
if ( this != &rhs ) {
this->~polymorphic_var();
new (this) polymorphic_var ( rhs );
}
return ( *this );
}
~polymorphic_var ( void ) {
the_destr_fct( the_data );
}
T & me ( void ) {
return ( the_ref_fct( the_data ) );
}
T const & me ( void ) const {
return ( the_ref_fct( const_cast<AllignedPod&>(the_data) ) );
}
}; // polymorphic_var
template < typename T, typename AllignedPod >
class polymorphic_vector {
typedef polymorphic_var<T,AllignedPod> my_var_type;
typedef std::vector< my_var_type > container;
container the_data;
public:
typedef T value_type;
typedef value_type & reference;
typedef value_type const & const_reference;
typedef value_type * pointer;
typedef value_type const * const_pointer;
typedef typename container::size_type size_type;
template < typename D >
void push_back ( D const & d ) {
the_data.push_back( my_var_type( d ) );
}
void pop_back ( void ) {
the_data.pop_back();
}
reference operator[] ( size_type i ) {
return ( the_data[i].me() );
}
const_reference operator[] ( size_type i ) const {
return ( the_data[i].me() );
}
size_type size ( void ) const {
return ( the_data.size() );
}
class iterator : public container::iterator {
friend
class polymorphic_vector;
typedef typename container::iterator base;
iterator ( base pos )
: base( pos )
{}
public:
typedef typename polymorphic_vector::value_type value_type;
typedef typename polymorphic_vector::reference reference;
typedef typename polymorphic_vector::pointer pointer;
reference operator* ( void ) const {
return ( base::operator*().me() );
}
pointer operator-> ( void ) const {
return ( & operator*() );
}
};
iterator begin ( void ) {
return ( the_data.begin() );
}
iterator end ( void ) {
return ( the_data.end() );
}
class const_iterator : public container::const_iterator {
friend
class polymorphic_vector;
typedef typename container::const_iterator base;
const_iterator ( base pos )
: base( pos )
{}
public:
const_iterator ( iterator pos )
: base ( pos )
{}
typedef typename polymorphic_vector::value_type value_type;
typedef typename polymorphic_vector::const_reference reference;
typedef typename polymorphic_vector::const_pointer pointer;
reference operator* ( void ) const {
return ( base::operator*().me() );
}
pointer operator-> ( void ) const {
return ( & operator*() );
}
};
const_iterator begin ( void ) const {
return ( the_data.begin() );
}
const_iterator end ( void ) const {
return ( the_data.end() );
}
typedef std::reverse_iterator< iterator > reverse_iterator;
reverse_iterator rbegin ( void ) {
return ( reverse_iterator( end() ) );
}
reverse_iterator rend ( void ) {
return ( reverse_iterator( begin() ) );
}
}; // polymorphic_vector
#include <iostream>
struct Base {
Base ( void ) {}
virtual
void id ( void ) const {
std::cout << "Base\n";
}
};
struct Derived : public Base {
Derived ( void )
: Base ()
{}
virtual
void id ( void ) const {
std::cout << "Derived\n";
}
};
typedef polymorphic_vector< Base, int > poly_vect;
int main ( void ) {
poly_vect p_vector;
Base b;
Derived d;
p_vector.push_back( b );
p_vector.push_back( d );
p_vector.push_back( b );
p_vector.push_back( b );
p_vector.push_back( b );
p_vector.push_back( d );
p_vector.push_back( d );
p_vector.push_back( b );
p_vector.push_back( d );
for ( poly_vect::size_type i = 0;
i < p_vector.size(); ++ i ) {
p_vector[i].id();
}
std::cout << '\n';
poly_vect p_vect_2 ( p_vector );
for ( poly_vect::const_iterator iter = p_vect_2.begin();
iter != p_vect_2.end(); ++iter ) {
iter->id();
}
/*
for ( poly_vect::reverse_iterator iter = p_vect_2.rbegin();
iter != p_vect_2.rend(); ++iter ) {
iter->id();
}
*/
}
This assumes that AllignedPod is a type that is suitable for holding objects
of all the derived types you want to use. The behavior is at least
implementation defined and maybe undefined at some places. The code is
horrible, and I would prefer the first solution any day.
Note that in addition to the data, we need to store three function pointers.
Best
Kai-Uwe Bux