473,513 Members | 2,583 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Polymorph in Place

I am considering a strategy for implementation of a finite state machine.
What I would like to do is to use derived classes to represent the state
of the machine, so the vtable pointer is the state and the virtual methods
are the inputs to the machine.

The heart of the issue is the following construct when changing state:

switch (newstate) {
case MT_IDLE: new(this) MT::IDLE(*this); break;
case MT_WAIT_ACK: new(this) MT::WAIT_ACK(*this); break;
case MT_WAIT_DATA: new(this) MT::WAIT_DATA(*this); break;
}
This is part of a SetState() method of the base class. The base class has
the following:

void *operator new(size_t, MT *mt) { return mt; }

so the placement new "allocates" the same memory that the object currently
occupies. The derived classes include a null copy constructor like:

IDLE(const MT &) {}

So, each line in the switch above just changes the vtable pointer (at least
with g++ with optimization, that is all that is generated).
Does this idea of "polymorphing in place" violate the C++ standard anywhere?
If so, is there any adjustment that could be done to make it compliant?

Is there anything that looks to be problematic with this? Certainly the
derived classes cannot be allowed to increase the memory footprint of the
class, and I should be able to check that statically at compile time in the
new() operator. Anything else that would be recommended?

Thanks in Advance!

Marcus Hall
ma****@tuells.org
May 10 '07 #1
4 1617
On May 11, 5:49 am, mar...@tuells.org (marcus hall) wrote:
....
>
The heart of the issue is the following construct when changing state:

switch (newstate) {
case MT_IDLE: new(this) MT::IDLE(*this); break;
case MT_WAIT_ACK: new(this) MT::WAIT_ACK(*this); break;
case MT_WAIT_DATA: new(this) MT::WAIT_DATA(*this); break;

this->~T() .... should be called before new. As long as you make sure
that the size of the block is correct (static assert or somthing here)
then this code *by itself* should do what you want.
}
I hear roars of chunder.
>
This is part of a SetState() method of the base class. The base class has
the following:

void *operator new(size_t, MT *mt) { return mt; }

so the placement new "allocates" the same memory that the object currently
occupies. The derived classes include a null copy constructor like:

IDLE(const MT &) {}

So, each line in the switch above just changes the vtable pointer (at least
with g++ with optimization, that is all that is generated).

Does this idea of "polymorphing in place" violate the C++ standard anywhere?
If so, is there any adjustment that could be done to make it compliant?

Is there anything that looks to be problematic with this?
Well, the only problem is that I'm sure the compiler considers the
vtable pointer const so wherever you use pointers to these objects you
would need to declare the pointer themselves as volatile (since you
can't declare the vtable pointer volatile) to avoid the compiler
caching the wrong vtable pointer.

Can I call this method the "roars of chunder" idiom ?

Certainly the
derived classes cannot be allowed to increase the memory footprint of the
class, and I should be able to check that statically at compile time in the
new() operator. Anything else that would be recommended?

Thanks in Advance!

Marcus Hall
mar...@tuells.org

May 10 '07 #2
On May 11, 7:03 am, Gianni Mariani <gi3nos...@mariani.wswrote:
On May 11, 5:49 am, mar...@tuells.org (marcus hall) wrote:
....
Is there anything that looks to be problematic with this?

Well, the only problem is that I'm sure the compiler considers the
vtable pointer const so wherever you use pointers to these objects you
would need to declare the pointer themselves as volatile (since you
can't declare the vtable pointer volatile) to avoid the compiler
caching the wrong vtable pointer.

Can I call this method the "roars of chunder" idiom ?
It's harder than this. The type of the "this" pointer to these
objects needs to be considered volatile. I.e - ALL the nonstatic
methods need to be specified as "volatile".

struct X {
virtual void f() volatile = 0;
};
May 10 '07 #3
On May 10, 9:49 pm, mar...@tuells.org (marcus hall) wrote:
I am considering a strategy for implementation of a finite state machine.
What I would like to do is to use derived classes to represent the state
of the machine, so the vtable pointer is the state and the virtual methods
are the inputs to the machine.
The heart of the issue is the following construct when changing state:
switch (newstate) {
case MT_IDLE: new(this) MT::IDLE(*this); break;
case MT_WAIT_ACK: new(this) MT::WAIT_ACK(*this); break;
case MT_WAIT_DATA: new(this) MT::WAIT_DATA(*this); break;
}
This is part of a SetState() method of the base class. The base class has
the following:
void *operator new(size_t, MT *mt) { return mt; }
so the placement new "allocates" the same memory that the object currently
occupies. The derived classes include a null copy constructor like:
IDLE(const MT &) {}
So, each line in the switch above just changes the vtable pointer (at least
with g++ with optimization, that is all that is generated).
Does this idea of "polymorphing in place" violate the C++ standard anywhere?
If so, is there any adjustment that could be done to make it compliant?
Yes and no.

First, I presume that the classes in question have virtual
functions. In which case, they also have a non-trivial
destructor, which (formally) must be called. And which means
that you cannot use *this as an argument to the constructor. So
you'd end up having to do something like:

// Save all essential information in local variables...
this->~Base() ;
switch ( newstate ) {
case MT_IDLE : new ( this ) MT::IDLE( /* saved information */ ) ;
break ;
// ...
}

Having done that, a lot depends on the context. If this is the
entire function, you're covered by the standard (supposing that
the actual memory has sufficient size and alignment for all of
the derived types). Trying to do anything further within the
function, however, is undefined behavior; the compiler has the
right to suppose that the type of *this doesn't change under
it's feet. (Note that this also means that you cannot call this
function from a member function of a derived class, and do
anything in the calling function afterwards. Something like:

MT::Base*
MT_SomeState::event( EventDescription const& event )
{
// ...
return changeState( newState ) ;
}

where changeState is the function above is OK, however.)

Whether this is a good idea is another question. Compilers can
often understand things that a human reader can't. I've often
found it useful to separate the functions and the data in such
cases, maintaining permenant instances of the polymorphic state
handling objects, and a separate instance of the data shared by
them. This avoids the switch, and allows keeping the instances
of the state in a table.

Why do you want to do this, rather than simply use dynamic
allocation? If it is purely for performance reasons, I suspect
that the allocation won't have a measurable impact. If you are
worried about fragmentation, a separate pool allocator could
take care of that---correctly designed, it could also handle any
possible performance issues as well.
Is there anything that looks to be problematic with this? Certainly the
derived classes cannot be allowed to increase the memory footprint of the
class, and I should be able to check that statically at compile time in the
new() operator. Anything else that would be recommended?
Alignment and size are the major considerations. Other than
that, you aren't allowed to execute any code where the this
pointer might point to memory of the wrong type, nor where the
type of the object pointed to by this changes.

Although the standard seems to say that it is legal (albeit not
too clearly), I'd also be sceptical of using a pointer to the
object after calling the polymorphing function, without an
intervening assignment. Given code like the above, I'd very
definitly write:

MT::Base* p = new MT::InitialState ;

while ( waitForEvent ) {
p = p->event( ... ) ;
}

even if I knew that the value returned by p->event was the same
as the original value of p. (In fact, I would definitly write
the code this way, with event returning a new dynamically
allocated object, and only change to using a memory pool or some
other strategy if the profiler said it was definitly necessary.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

May 11 '07 #4
"Gianni Mariani" <gi*******@mariani.wsha scritto nel messaggio
news:11**********************@e65g2000hsc.googlegr oups.com...
Well, the only problem is that I'm sure the compiler considers the
vtable pointer const so wherever you use pointers to these objects you
would need to declare the pointer themselves as volatile (since you
can't declare the vtable pointer volatile) to avoid the compiler
caching the wrong vtable pointer.
I actually got curious about this and tested it.
Everything worked perfectly fine without declaring *anything* as "volatile"
(MSVC++ 2005).
Massimo

May 15 '07 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

8
2938
by: Andrew Morgan | last post by:
Hi I have a matrix class: template<class Type> class TMatrix { //... TMatrix& T(); //... }
9
1446
by: Mika Vainio | last post by:
hi everybody, i'm working on the following problem: i need to build a 26-nary tree to save a data dictionary. every letter of the words is represented by a cell (cell has a pointer-vector cell*...
1
1753
by: Sabine Oebbecke | last post by:
Hi there, Need some help again ... I have a continuous form which shows the attendees of a competition as per their placing in the competition. So, the first record is the winner, the second...
2
1354
by: Frank Jeseit | last post by:
Hi, I have two methods, with the same name but different params: MyMethod(string) MyMethod(string, string, string) I can build the service but the call in IE or a client app fails, because...
10
5304
by: Water Cooler v2 | last post by:
I've asked myself this question in the past but couldn't afford more time to it (I program other languages for my bread and butter), so I'll ask now since it's never too late. What does the...
5
1965
by: Ian Bicking | last post by:
I got a puzzler for y'all. I want to allow the editing of functions in-place. I won't go into the reason (it's for HTConsole -- http://blog.ianbicking.org/introducing-htconsole.html), except that...
2
1480
by: Fred | last post by:
I've got the following code: #include <iostream> class Base{ private: virtual void f(int) { std::cout << "f in Base" << std::endl; } };
1
4444
by: MindWrapper | last post by:
boost serialization of polymorph classes from DLLs Folks, Let's consider following code -------------------------------- // base.h // abstract base class class IBase {
0
1030
by: Terry Reedy | last post by:
Arash Arfaee wrote: Python does not have 'in-place operators'. It has 'augmented assignment statements' that combines a binary operation with an assignment. *If* the target being rebound is...
0
7160
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
7537
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
7099
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
7525
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
1
5086
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
4746
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and...
0
3233
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
3222
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
0
456
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.