Re: Object layout guarantees and manual pointer adjustments
Hi!
SG schrieb:
Admittedly, it's a rather ugly "solution". But so far I wasn't able
to come up with a less ugly implementation that meets all the design
goals. It's more of a puzzle right now. I'm aware that it may not be
very useful in practise.
Ok, I was able to clone all information and not rely on object layout. I
introduced a new class hierachy of converter objects, that implement a
function from abstract_wrapper* to T& by means of a simply linked list
of converters. Every time a cow<T> is constructed from a cow<U> a
converter from U& to T& is instantiated and added to the list. Then you
can retrieve the cloned T& from the new instance of abstract_wrapper.
The downside is the long execution path on converting cow<T>. Memory
allocation can be reduced, I think.
Code follows:
#include <iostream>
#include <ostream>
#include <string>
namespace proposed
{
class abstract_wrapper
{
public:
virtual ~abstract_wrapper() {}
virtual abstract_wrapper* clone() =0;
};
template<typename U>
class concrete_wrapper
: public abstract_wrapper
{
U u;
public:
U& get() { return u; }
concrete_wrapper<U>* clone()
{ return new concrete_wrapper<U>(*this); }
};
template<typename TargetType>
struct converter_base
{
converter_base() {}
virtual ~converter_base() {}
virtual converter_base* clone() =0;
virtual TargetType& get(abstract_wrapper*) =0;
protected:
converter_base(converter_base const&) {}
};
template<typename SourceType>
struct root_converter : converter_base<SourceType>
{
root_converter<SourceType>* clone()
{
return new root_converter<SourceType>();
}
SourceType& get(abstract_wrapper* const aw)
{
return static_cast<concrete_wrapper<SourceType>*>(aw)->get();
}
};
template<typename TargetType, typename SourceType>
struct converter : converter_base<TargetType>
{
typedef converter_base<SourceType> referred_type;
converter(referred_type* next)
: next(next)
{}
~converter() { delete next; }
converter<TargetType, SourceType>* clone()
{
return new converter<TargetType, SourceType>(next->clone());
}
TargetType& get(abstract_wrapper* const aw)
{
return next->get(aw);
}
private:
referred_type* next;
converter(converter const&) /*{}*/ ;
};
template<typename T>
class cow
{
abstract_wrapper* wrapper;
converter_base<T>* converter;
public:
cow()
{
wrapper = new concrete_wrapper<T>();
converter = new root_converter<T>();
}
cow(cow const& other)
: wrapper(other.cloneWrapper())
, converter(other.cloneConverter())
{
}
~cow()
{
delete converter;
delete wrapper;
}
template<typename Other>
cow(cow<Other> const& other)
: wrapper(other.cloneWrapper())
, converter(new proposed::converter<T, Other>(other.cloneConverter()))
{
}
abstract_wrapper* cloneWrapper() const { return wrapper->clone(); }
converter_base<T>* cloneConverter() const { return converter->clone(); }
T* operator -> () const { return &converter->get(wrapper); }
T& operator * () const { return converter->get(wrapper); }
};
} //namespace proposed
struct Base
{
virtual std::string get() =0;
};
struct Dev : Base
{
std::string get() { return a; }
std::string a;
};
int main()
{
using namespace proposed;
cow<Dev> d;
d->a = "Hallo";
cow<Base> b(d);
std::cout << b->get() << std::endl;
}
valgrind didn't report any memory leaks on my machine for this sample
program. I also had a namespace "given" which I left out now. It
contained the implementation of the class diagram you posted. I hope you
don't mind the namespace "proposed" here.
HTH,
Frank
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]