ANY time you allocate memory from the heap, malloc WILL be used, whether directly by you, or by some other function you call. There is no magical way of allocation heap memory that doesn't use malloc.
?
If I may add some drivel, the OP is trying to use templates to bind an array size at compile-time. That is one alternative to using the heap. There are other run-time allocation techniques besides the heap (i.e., malloc and free).
There is NOTHING wrong with malloc
Actually, it is the worst dynamic memory technique, and its problems are well documented. It is possible to use it safely, but that usually requires a good deal of expertise and care.
The OP has not fully described the objects' lifetimes, but if the allocations are made only during setup, then it is quite safe to use. And you must resist the tempatation to realloc, "just this once."
[Edit: I see that this is the case.]
However, if the allocations happen at various times after that, with overlapping lifetimes and different sizes, then it is not safe to use. Allocating random sizes from the heap, at random times, is a sure-fire way to court failure.
NOTHING to be gained by trying to avoid using it.
Actually, you would gain 730 bytes of program space and 4 bytes of RAM per allocation.
the alternate seems to be complex and messy simpleFIFO made it look easy but its requires much more than I expected to get functional code.
Yes, C++ templates usually require the implementations (code for each method) to be in the header. For something like a FIFO, it can be a good solution. For larger, more complex classes, it is not as readable or manageable, IMO.
FWIW, it is important to analyze the buffer requirements, because an embedded system needs to be implemented in a predictable way: determinism is your best friend. Understanding the maximum buffer sizes and maximum number of buffers allows you to dictate (i.e. predict) the response when those limits are reached.
From your last post it appears that you have done some of this analysis. Can I ask why you don't want to pass the storage into the constructor? That isn't much different from passing the size, especially if it only happens at init time (at file scope, before setup):
class ComProtocol
{
uint16_t Size;
uint8_t Buffer;
public:
ComProtocol()
: Size(0), Buffer( (uint8_t *)NULL )
{};
ComProtocol( uint16_t size, uint8_t buffer )
: Size(size), Buffer( buffer )
{};
// ... methods...
};
static uint8_t fooBuffer[100];
static uint8_t barBuffer[200];
ComProtocol foo( sizeof(fooBuffer), fooBuffer );
ComProtocol bar( sizeof(barBuffer), barBuffer );
This is a common technique, and can be made even easier to use with a #define:
#define COM_PROTOCOL(name,size) \
static uint8_t name ## Buffer [ size ]; \
ComProtocol name( sizeof( name ## Buffer ), name ## Buffer );
And in use:
COM_PROTOCOL( foo, 100 );
COM_PROTOCOL( bar, 200 );
All together, the sketch would be:
class ComProtocol
{
size_t Size;
uint8_t *Buffer;
public:
ComProtocol()
: Size(0), Buffer( (uint8_t *)NULL )
{};
ComProtocol( size_t size, uint8_t *buffer )
: Size(size), Buffer( buffer )
{};
// ... methods...
};
#define COM_PROTOCOL(name,size) \
static uint8_t name ## Buffer [ size ]; \
ComProtocol name( sizeof( name ## Buffer ), name ## Buffer )
COM_PROTOCOL( foo, 100 );
COM_PROTOCOL( bar, 200 );
void setup() {}
void loop() {}
On an UNO, this uses 536 bytes of program space and 317 bytes of RAM.
The corresponding heap sketch would be this:
class ComProtocol
{
size_t Size;
uint8_t *Buffer;
public:
ComProtocol()
: Size(0), Buffer( (uint8_t *)NULL )
{};
ComProtocol( size_t size )
{
Buffer = (uint8_t *) malloc( size );
if (Buffer)
Size = size;
else
Size = 0;
};
~ComProtocol()
{
if (Size && Buffer)
free( Buffer );
}
// ... methods...
};
ComProtocol foo( 100 );
ComProtocol bar( 200 );
void setup() {}
void loop() {}
This uses 1270 bytes of program space and 335 bytes of RAM (27 + 300 + 2*4).
Cheers,
/dev