Go Down

Topic: Rascal (Read 5969 times) previous topic - next topic


Dynamic polymorphism has nothing to do with new

My C++ may be rusty, but I don't remember any other method (than new) to create objects dynamically (at runtime).


My C++ may be rusty, but I don't remember any other method (than new) to create objects dynamically (at runtime).

You are correct in regards to dynamic allocation of memory ( stack space can be used in a pseudo-dynamic fashion ). However dynamic polymorphism is not related to allocation of memory.

Dynamic polymorphism is a way of using types to provide a layer of commonality between one or many incompatible types.The dynamic part of this is the need for a v-table to provide the relationship between interface and implementation. Static polymorphism requires each layer of abstraction to be statically typed to its derived implementation. The difference is the loss of v-table overhead and also the dynamic ability to reference incomplete types.

Each paradigm has its own pros and cons.


OK, thanks.
So my question to you then (maybe I should start a new thread) is how do you implement dynamic polymorphism without new?
(I am interested in practical solutions. I don't want to re-implement new() using malloc, create vtables etc, mechanism that is already provided by the C++ compiler and linker).


I will try my best to give a good explanation.

Using objects can mean a few things, but to start,  I will clarify that we are talking about using new to create dynamic instances of a polymorphic class rather than polymorphic classes using new to create storage. In my opinion new() is not something I will use on a micro-controller. I have found that due to their limited resources, designs can benefit from a static-as-possible approach ( memory allocation ).

After a while of failing to divise a nice concise example, I will instead just try to explain my current project as polymorphism is something that is specific to a cause, it can't just be applied to something as an optimisation endevour.

I have a pseudo-HAL system that abstracts the arduino hardware ( transport systems ) at a very low level while providing hardware independant access to it. My main goal is to have an optimal arduino implementation that is portable between 8 and 32 bit platforms ( Atmel 8-bit, Due & STM32 ).

I start off at the very bottom with a transport layer. It defines an interface for update and access of hardware data ( no specific hardware ).
Next are input and output classes that  derive the transport class and provide an interface for how to read or write data to and from hardware ( also, no specific hardware ).
At the end of the transport chain there is the actual hardware, I have currently implemented classes for ShiftIn & ShiftOut, Parallel ( read/write system ), SPI ( read/write also ), i'm also part way through SPIShift ( read/write ). These classes talk to the hardware and provide the data in a way that is compatible with the transport layer interface.

The transport system has been implemented in both dynamic and static versions.

The hardware specific transport classes access the actual physical hardware through a single interface that is hardcoded to the platform. It is the only part of the system that has to be reproduced for different platforms.

This may seem like a lot of work ( it was ) for not much advantage apart from being able to access different hardware under a common interface, especially when accessing the hardware directly through its class. For example using a 74hc595 and the ShiftOut class, its first advantage is the fact it directly accesses the hardware so it is much faster than using the arduino standard shiftOut, even with the overhead of the virtual function calls it is still many times faster. Now thats using the dynamic method. Using static polymorphism the hardware classes will emit instructions equivalent to explicitly writing out the direct port mapping yourself. The transport layer completley dissapears, and your code will work on all arduinos.

The whole reason behind the transport system isn't just to allow easy access to the hardware in a portable and fast way, but for an even higher purpose I will rant on about now.

One project I'm working on involves an LCD ( ST7920 ) and I want to use it with a big system, for now it looks to fit on an UNO, but maybe I will end up needing the ram and outputs of a MEGA, or even the non-existant Due. The point I want to make is each of these systems has a potentially different way of connecting the LCD more efficiently.

So rather than create a different implementation for each different connection method, the LCD driver talks to the transport layer interface instead. When creating the LCD instance you specify the transport class it is going to use. So to change the connection method from parallel on a Mega to shift in/out on an UNO you only need to modify one line of code.

To show an example, here is an excerpt from the library LCD12864, it is the code it uses to talk to the LCD. Below that is the same piece of code but using my library, as you can see it has nothing to do with any particular connection method.

Code: [Select]
void LCD12864::setPins(uint8_t tRS, uint8_t tRW, uint8_t tD7, uint8_t tD6, uint8_t tD5, uint8_t tD4, uint8_t tD3, uint8_t tD2, uint8_t tD1, uint8_t tD0) {

  digitalWrite(RS, tRS);   
  digitalWrite(RW, tRW);   
  digitalWrite(D7, tD7);   
  digitalWrite(D6, tD6);   
  digitalWrite(D5, tD5);   
  digitalWrite(D4, tD4);   
  digitalWrite(D3, tD3);   
  digitalWrite(D2, tD2);   
  digitalWrite(D1, tD1);   
  digitalWrite(D0, tD0);   
  digitalWrite(EN, 0);   


Mine ( comments are part of code not for my post here ):
Code: [Select]
  BASE_TEMPLATE void BASE_TYPE::_ByteOut( byte b_Data )
        FLAG_ON( ST7920_EN );  //this->t_Output.Write( true, _WriteOffset + ST7920_EN );
        this->t_Output[ ST7920_B0 ] = b_Data;
        FLAG_OFF( ST7920_EN );  //Significant speed increase here over: this->t_Output.WriteBit( false, _WriteOffset + ST7920_EN, true );

Just like my previous example, a test of most LCD features using static polymorphism discovered that not only the transport layer but also the LCD class almost entirely disolved. The only major indirection from linear program flow was the shiftout code as it is used with every lcd command, all unique lcd stuff was inlined directly into the calling code.

Now after all this you may ask yourself 'what has this got to do with new()', well the answer is nothing. You can use these components dynamically if you want, but as they describe a static system, you gain no benefit over initialising a global variable. All the features I rambled on about are only a fraction of the libraries actual capabilities, it employs both dynamic and static polymorphism. And this is where I think you are mixing things up. Memory allocators like new() are working with data whereas polymorphism is entirely types, the explanation I was trying to make at the start will now make sense.

At first glance you may notice from my explanations that the static version is faster and more concise. This is great but not always the best option.

Lets take a scenario made simple by the library: Multiple LCD's connected to a single arduino ( doesn't matter which board or connection type ). There are a couple of different modes that may be common.

1. Each LCD displaying its own data, or
2. Both LCD's displaying the same thing.

Either method can be done using static polymorphism. However only the first is more efficient than the dynamic version in terms of scalability. As the static version removes the v-table it produces more direct code, or most code will be replicated between the LCD's doing the same thing.

The dynamic version comes into play as it would allow both LCD's to be referenced in an array of the base transport type. So each LCD could have different connection methods, and still have the hardware code in a single location as the v-table maps the transport interface to the hardware implementation.

Hope this helps.


Thanks. I need some time to digest what you wrote.
My first thought was about (source) code readability and accessibility. Many years ago I learned to write code for others rather than me. You may understand your code, but if the person who takes over doesn't, then that code becomes un-maintainable (or very expensive to maintain). Good job security though :)

BTW, you should gather these in your blog. Many would be interested. Class diagram(s) (or any other kind of diagram) would also definitely help.


The readability ( once tidied up and presented well with comments ) should not suffer, I have created it in a way which will hopefully encourage people to extend its functionality, not just use it to improve external library development. I would say that most of the code is just pure interface, just discrete objects encapsulating a single functionality. I also plan to hide the interface behind some #define's to allow a linear looking usage for those not keen on working with templates. I also have devised a system for some debugging and error handling.

I was planning on pre-releasing it shortly after the release of the Due, I haven't got a blog, but I can have a look at starting one. Unfortunately my design data is a scrapbook. I'm planning on finding a visio style program for linux to do up my diagrams.

It is encouraging to read your comments so in the next couple of days I'll try to get a detailed description with some examples into the 'software development' forum to see what others think. I have noticed a lot of people are slightly against c++ for different reasons, so I have been holding off a release until I had a fully functional proof-of-concept.  I've been working on it randomly for five months now so its probably time to get some feedback anyway.


I'm planning on finding a visio style program for linux to do up my diagrams.

Take a look at dia...  I find it very similar to visio for functionality like that.


cheers, I've looked at its home page and it seems like it'll suit my needs fine.

Go Up