Doing a "delayed construction" (and alignment issues)

V

Virchanza

OK so let's start off with a class which has already been written and
which CANNOT be changed:

class MemberClass {

protected:

string const some_really_important_string;

public:

MemberClass(string const &arg) : some_really_important_string(arg)
{}
};

(Note that "some_really_important_string" is protected and that we
don't have any access to it at all after the construction of the
object)

Now let's say we have another class called DaddyClass which contains a
member object of the type "MemberClass", as follows:

class DaddyClass {

public:

MemberClass mc;

DaddyClass(string const &arg) : mc(arg) {}

};

This works fine... but what if we have to do some processing on the
"arg" string before we create the "mc" object? Also, another
requirement is that we can't change the interface of the DaddyClass
(i.e. it must still have a member object called "mc").

We could start off by resorting to using a pointer along with dynamic
memory allocation:

class DaddyClass {

public:

MemberClass *p_mc;

DaddyClass(string const arg)
{
/* First do some processing on "arg" */

p_mc = new MemberClass(arg);
}

~DaddyClass()
{
delete p_mc;
}
};

This proposed solution has two problems though:
1) The interface of DaddyClass has been changed, it no longer has a
member object called "mc".
2) There's the performance hit of using dynamic memory allocation.

Problem 1 can be solved as follows:

class DaddyClass {

protected:

MemberClass *p_mc;

public:

MemberClass &mc;

DaddyClass(string const arg) : p_mc( static_cast<MemberClass
*>( malloc(sizeof *p_mc) ) ),
mc(*p_mc)
{
if (!p_mc)
throw bad_alloc();

/* First do some processing on "arg" */

try
{
::new(p_mc) MemberClass(arg);
}
catch(...)
{
free(p_mc);
throw;
}
}

~DaddyClass()
{
mc.~MemberClass();

free(p_mc);
}
};

This has the tricky issue though of whether it's OK or not to bind
that "mc" reference before the MemberClass object has been
constructed. Strictly speaking, I think the Standard says the
behaviour is UB. In the real world though, it should be fine unless
there's some crazy debugging stuff going on in the background.

Anyway let's try another solution. Let's try to get rid of the dynamic
memory allocation.

In a world where alignment issues don't exist, we could have:

class DaddyClass {

protected:

char mem_for_mc[sizeof(MemberClass)];

public:

MemberClass &mc;

DaddyClass(string const arg) :
mc( *static_cast<MemberClass*>( static_cast<void*>(&mem_for_mc) ) )
{
/* Do some complicated processing on "arg" first... */

::new(&mc) string(arg);
}

~DaddyClass()
{
mc.~MemberClass();
}
};

(Again this has the issue of binding a reference to a non-existent
object)

To get around the alignment issues, this would have to be changed to
something like:

template<class T>
class StorageFor {

union Strict_Alignment {

bool *p; char *p1; long long *p2; float *p3; double long *p4;

bool z; char a; long long d; float e; double long g;
};

Strict_Alignment storage[sizeof(T) / sizeof(Strict_Alignment) + !!
( sizeof(T) % sizeof(Strict_Alignment) )];

public:

T *Pointer()
{
return static_cast<T*>( static_cast<void*>(&storage) );
}
};

class DaddyClass {

protected:

StorageFor<MemberClass> mem_for_mc;

public:

MemberClass &mc;

DaddyClass(string const arg) : mc( *mem_for_mc.Pointer() )
{
/* Do some complicated processing on "arg" first... */

::new(&mc) MemberClass(arg);
}

~DaddyClass()
{
mc.~MemberClass();
}
};

Anyway...

anyway......

anyway.........

When programming in C++, what are you *supposed* to do when you want
to preserve the interface of the DaddyCass, while at the same time
delaying the construction of a member object until some processing has
been done in the DaddyClass's constructor?

Would you say that the DaddyClass is inherently flawed because it
should not expose member objects to the user? Should it use member
functions instead, e.g.:

MemberObject &Get_mc()
{
return mc;
}

If DaddyClass is flawed and needs to be changed, then we could change
it to:

class DaddyClass {

protected:

StorageFor<MemberClass> mem_for_mc;

public:

MemberClass &Get_mc()
{
return *mem_for_mc.Pointer();
}

DaddyClass(string const arg)
{
/* Do some complicated processing on "arg" first... */

::new( mem_for_mc.Pointer() ) MemberClass(arg);
}

~DaddyClass()
{
mem_for_mc.Pointer()->~MemberClass();
}
};

Any thoughts?

Oh by the way, do I actually need to use that "StorageFor" class or is
there another way of doing it?
 
L

Larry Evans

On 12/14/10 08:45, Virchanza wrote:
[snip]
If DaddyClass is flawed and needs to be changed, then we could change
it to:

class DaddyClass {

protected:

StorageFor<MemberClass> mem_for_mc;

public:

MemberClass &Get_mc()
{
return *mem_for_mc.Pointer();
}

DaddyClass(string const arg)
{
/* Do some complicated processing on "arg" first... */

::new( mem_for_mc.Pointer() ) MemberClass(arg);
}

~DaddyClass()
{
mem_for_mc.Pointer()->~MemberClass();
}
};

Any thoughts?
I think:

boost::aligned_storage
< sizeof(MemberClass)
mem_for_mc;

where boost::aligned_storage is described here:


http://www.boost.org/doc/libs/1_45_...ost_typetraits/reference/aligned_storage.html

and boost::alignment_of is described here:


http://www.boost.org/doc/libs/1_45_.../boost_typetraits/reference/alignment_of.html

should work.
Oh by the way, do I actually need to use that "StorageFor" class or is
there another way of doing it?

I don't know of another way.

HTH.

-Larry
 
G

Goran

OK so let's start off with a class which has already been written and
which CANNOT be changed:

class MemberClass {

protected:

    string const some_really_important_string;

public:

    MemberClass(string const &arg) : some_really_important_string(arg)
{}

};

(Note that "some_really_important_string" is protected and that we
don't have any access to it at all after the construction of the
object)

Now let's say we have another class called DaddyClass which contains a
member object of the type "MemberClass", as follows:

class DaddyClass {

public:

    MemberClass mc;

    DaddyClass(string const &arg) : mc(arg) {}

};

This works fine... but what if we have to do some processing on the
"arg" string before we create the "mc" object?

Erm...

string PreProcess(const string& arg)
{
...
return result;
}

DaddyClass::DaddyClass(string const &arg) : mc(PreProcess(arg) {}

Alternatively, since you can change DaddyClass, and in any more
complicated situation, you can simply insert "parameter object" that
would replace PreProcess and would do any additional processing on
entering DaddyClass ctor. For example, if you need to use PreProcess-
ed arg, put it in a temporary that you create when entering DaddyClass
ctor.

Goran.
 
V

Virchanza

Erm...

string PreProcess(const string& arg)
{
  ...
  return result;

}

DaddyClass::DaddyClass(string const &arg) : mc(PreProcess(arg) {}


Haha brilliant.

That's what I love about posting to forums like this, you get to hear
an idea that you just simply hadn't thought of!

I'll play around with my code and see if I can work this in!
 

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

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,042
Latest member
icassiem

Latest Threads

Top