C++ class constructors vs. initializers vs. init method

Go To Last Post
49 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Why do we NEED, in C++, to have both class initilizers as an option, and class constructors as an option?

For embedded systems, a class constructor is deadly, if it does I/O things that can go wrong. The constructors are called before the system is fully initilialized, e.g., there's usually no LEDs or printf() available to the constructor.

The C++ class instancing using initializers is confusing and secret-sauce coding. Which I hate.

 

Too bad we don't have a convention like myclass.init().

We still have the convention of main()!

 

I am weary of OOP now, after months of spending 80% of my time struggling with large bodies of code where the author, IMO, way overused inheritence and subclassing - perhaps because it was clever.

 

Last Edited: Wed. Jan 7, 2015 - 12:19 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For the same reason we need to have initialized simple variables?

 

For embedded systems, a class constructor is deadly ... The constructors are called before the system is fully initilialized

That's what happens in avr-g++, but I don't think it NEEDS to be the case.  Maybe avr-g++ is just not well designed in that area.

(although I'll admit that allowing random and perhaps large amounts of user code to execute before main() is worrisome in any case.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I new the classes in the proper order.  I use the constructors where applicable.  I seldom do the .init() thing. 

 

I don't use those "static" classes or whatever you call them.  I don't want anything to be initialized before I tell it to.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ha! Funny how different folks view things...

 

I always use static classes with small-RAM microprocessors. Can't deal with run time heap usage. OK for megabyte RAM systems!

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

All C++ work I've done with 3 different compilers, including ARM M4, the constructors are called right after initialized RAM variables are written. But before things are setup.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

stevech wrote:

Ha! Funny how different folks view things...

 

. Can't deal with run time heap usage. OK for megabyte RAM systems!

 

 

It takes a couple of C statements to allocate from the heap.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why do we NEED, in C++, to have both class initilizers as an option, and class constructors as an option?

For embedded systems, a class constructor is deadly, if it does I/O things that can go wrong.

Didn't you just answer your own question? There are times when the class constructor is run before other external dependancies are ready. A class initializer  function can be called when it is known that everything is ready.

The constructors are called before the system is fully initilialized,

Only for global variables. 

Regards,
Steve A.

The Board helps those that help themselves.

Last Edited: Wed. Jan 7, 2015 - 05:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

a class constructor is deadly, if it does I/O things that can go wrong

So don't do it in the constructor ;-)

 

Just like when you write:

int foo = 12345;

and _do_copy_data in the CRT arranges for 12345 to be placed into location 'foo' for you then C++ c'tors are little more than that - your opportunity to initialize some members. Leave other stuff for later.

 

While this can mean having a class.init() method you don't have to do it that way. If you have some kind of class.execute()/class.do_your_stuff()/etc. then just have it check a bool for initialized==true ? If the class (ie IO hardware etc) is not initialized then do that work on the first call to execute/do_your_stuff/whatever...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Constructor failure is always a huge issue even on PCs.

Never ever put anything in a constructor that can fail.

Never call other constructors in a constructor.

( and pay attention to temporary objects)

 

It IS how ever most excellent for initializing variables and state.

 

 and you should be using the initializer list like;

MyClass::MyClass():

variable1(0),

variable2(42)

{

}

 

even when writing windows code with RTTI and excceptions I use 2 step initialization.

the constructor initializes the class

The init function takes care of memory allocation and constructing other objects.

 

Keith Vasilakes

Firmware engineer

Minnesota

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You guys with your statics are crazy.  I think some of you confuse new with malloc().  Don't do that.  new is an operator that can be associated with anything in the operator function. 

 

Keep in mind if you learned to program in school, your teacher was probably a grade school dropout that couldn't get a real job.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Never call other constructors in a constructor.

My C++ knowledge is still nascent but isn't it kind of inevitable that constructors will call other constructors if they have any member variables that are themselves a class? So you may not be able to avoid c'tor calling c'tor.

 

This illustrates what I mean:

$ cat test.cpp
#include <stdio.h>

class foo {
public:
  foo() { printf("foo contruct\n"); }
};

class bar {
public:
  bar() { printf("bar construct\n"); }
private:
  foo m_foo;
};

bar g_bar;

int main(void) {
  printf("entry into main\n");
}

$ gcc test.cpp -o test
$ ./test
foo contruct
bar construct
entry into main

I only instantiate g_bar of the bar class type but, because it has a foo member the foo() constructor is called too. Actually the surprise for me here is that the foo constructor is called before the bar constructor. I guess the member variables are being created (and any c'tors called) before the c'tor for the class that holds them is called?

Last Edited: Wed. Jan 7, 2015 - 03:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My C++ knowledge is still nascent but isn't it kind of inevitable that constructors will call other constructors if they have any member variables that are themselves a class?

Unless Keith was referring to calling a different constructor of the same object. 

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Oh dear - I'm guilty of that - sometimes I have the c'tor overloaded with several interfaces but most just do something with more/less parameters then go on to call the core c'tor where most of the common work is done.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

An AVR starts with everything accessible.

Some Cortex-M0 chips start with nothing running.   e.g. no clock to GPIO

 

So constructor()s need to be very careful on those particular ARM chips.   e.g. Freescale

 

I have no idea how Atmel Cortex chips work,   or even the UC32 range.

 

So there is definitely a requirement for different types of initialisation.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:

My C++ knowledge is still nascent but isn't it kind of inevitable that constructors will call other constructors if they have any member variables that are themselves a class?

Unless Keith was referring to calling a different constructor of the same object. 

 

Yes the default constructor is being called on the member objects.

This means you should not be doing anything risky in your default constructor.

only variable initialization basically.

No new()

no complex initializations

 

Two step construction is the safe(r) way.

exceptions / failures in constructors are extremely nasty and hard to manage.

Talk to some compiler writers about stack unwinding during a constructor exception and be prepared for a long discussion....

 

 

Keith Vasilakes

Firmware engineer

Minnesota

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also, I'd rate the ability to create complex data objects as easily as C can create variables (including initialized globals) to be a really important aspect of an object-oriented language.

 

I think some of you confuse new with malloc().  Don't do that.  new is an operator that can be associated with anything in the operator function. 

That's sort of a scary thought.  Do you have an example of when that it is a good thing?  (and I thought that was the essence of the original complaint - by having a constructor do MORE than memory allocation and initialization, you run the risk of doing things that aren't actually available at that time.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I get software written by others that does things in constructors that are likely to sometimes fail. Like GPIO setup or outputs, or reading something when the wiring is wrong.

Cause stuck in a loop with no visual clues.

No common sense.

 

Worse, the damned class variables' initializers and their syntax. Hate that.

 

Therefore, I don't do much of anything in the constructors or initializers and try to not let the compiler do secret things. Like calling a list of constructors at all, or calling them in the wrong order.

I never use New or malloc(); essentially, New calls malloc() or some such to get RAM from the heap.

Not embedded systems-friendly.

 

Last Edited: Thu. Jan 8, 2015 - 01:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

New calls whatever you want it to call.  It's only requirement is that it return the address of the object you new.   New is good.  Much better than static classes.   malloc() has little or no use in embedded code.

 

If you want to allocate from the heap and later return it to the heap, you need malloc() and free().  You also need your head examined if you are writing embedded code.

Last Edited: Thu. Jan 8, 2015 - 02:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So... are you recommending that "new" for an object return a pointer to one of a set of pre-allocated static objects?  "return &myobjs[Nmyobjects++];"

 

malloc() has little or no use in embedded code.

I'm so confident that my home-made object allocator will be MUCH more bug and trouble-free than malloc()!

 

(Hmm.  malloc() isn't so bad.  It's free() that's a problem!  So is there a reference implementation of a malloc-like function that trivially assigns chunks of memory without allowing for it to be free'd later?  It sounds like it should be trivial, but I bet there could be subtleties.  (freememP += n; return freememP-n; ?))

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I'm an old school "hacker" and I think we are all talking about avr-g++ here can I just mention .init3 or possibly .init5 ? Problem solved.cheeky

 

(sorry but it's very hard for an old Asm/C programmer to switch to the C++ way of thinking!)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here are the global new operator functions that I use.  These allocate from the heap, except for the placement new of course.   These things have been around avrfreaks for a long time.  I based these on ones I saw on freaks circa 2005.  I don't remember who the poster was.

 

You can also put new operator functions in a class.  If they are present, they will be used instead of the global ones.  I'll give an example in another post.

 

I'll give you this simple global one first to make it easy to understand.  This is all you will probably need 98.6% of the time.

 

 


//  global new operator function.   This is a .ccp file to include in a project

#include "stdlib.h"    // for size_t

extern char __heap_start;          /// for avr-gcc
char* heap_next_p = &__heap_start;



void* AllocateFromHeap_p(size_t objsize)   {
   void* here_p = heap_next_p;
   heap_next_p += objsize;
   return here_p;
   }


void* operator new(size_t objsize)   {      // global new operator function gets memory from heap
   return AllocateFromHeap_p(objsize);
   }

 

 

If you want to use array new or placement new, include this in your project instead.

 



//  global new operator functions
//  Note this is a use once heap allocator.  If you want to recycle the heap you must use malloc() and free().

#include "stdlib.h"    // for size_t

extern char __heap_start;          /// for avr-gcc
char* heap_next_p = &__heap_start;


void* AllocateFromHeap_p(size_t objsize)   {
   void* here_p = heap_next_p;
   heap_next_p += objsize;
   return here_p;
   }


void* operator new(size_t objsize)   {        // regular allocate from heap function
   return AllocateFromHeap_p(objsize);
   }


void* operator new(size_t objsize, void* here_p)   {    // placement new
   return here_p;
   }


void* operator new[](size_t objsize)   {                // array new
   return AllocateFromHeap_p(objsize);
   }


void* operator new[](size_t objsize, void* here_p)   {  // array placement new
   return here_p;
   }


#if 0
void operator delete(void* obj)   {         // If you want to use delete, this will allow it to compile.
   }                                        // Note this doesn't return anything to the heap.
#endif

 

 

If you take a close look at C++ programs you write for your PC, you will find these same new operator functions there.  But instead of calling AllocateFromHeap_p(), they call malloc().

Last Edited: Thu. Jan 8, 2015 - 12:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here is an example of a class that contains it's own new operator function.  This is my Reset_regs class for the Xmega.  Notice the new operator function at the end of the class.  It simply returns the address of the Xmega's reset registers.   I can new this thing a million times.  It doesn't use up any heap memory.   It just returns the address.

 

Note, I'm not suggesting you do this for I/O registers unless you want to.  The code for flipping bits in the registers is likely to be less efficient than using the more orthodox methods like ASF for instance.

 

A new operator function in a class might also be useful for other reasons.  Maybe you want to allocate memory for it, but from somewhere other than the heap.  Maybe shared memory, etc.

 

As far as using new to get the address of a static class, I don't know.  The static class would need to return it's own address.  I suppose that would be the address of it's first data member.  Experimentation would be required as far as I'm concerned.  If you are smart enough to know it's a silly idea, so be it.

 


#ifndef RESET_REGS_H
#define RESET_REGS_H
#include <stdlib.h>     // for size_t in new and delete operators
#include <avr/io.h>
#include "myTypes.h"
#include "cpu_regs.h"


class Reset_regs   {
friend class Reset_simulator;
friend class Reset_mon;
friend class Jump_to_bootloader_mon;

private:
   union Status   {
      struct  {
         Register power_on                 : 1;  // PORF
         Register external                 : 1;  // EXTRF
         Register brownout                 : 1;  // BORF
         Register watchdog                 : 1;  // WDRF
         Register PDI                      : 1;  // PDIRF
         Register software                 : 1;  // SRF
         Register reserved                 : 2;  // Reserved
         } bits;
      u_int8 byte;
   };

   volatile Status status;                             // STATUS  0x00


   struct Control   {
      Register software                    : 1;  // SWRST
      Register reserved                    : 7;  // Reserved
      };

   volatile Control control;                           // CTRL    0x01


public:


   void Do_software_reset()   {
      Cpu_regs* cpu_regs_p = new Cpu_regs;
      cpu_regs_p->Disable_configuration_change_protection();
      control.software = true;
      }


   bool Is_software_reset()   {
      return status.bits.software;
      }


   void Clear_software_reset()   {
      Status my_status;
      my_status.byte = 0;
      my_status.bits.software = true;
      status.byte = my_status.byte;
      }


   void Clear_resets(u_int8 which)   {
      which ^= status.byte;
      status.byte = which;
      }


   void* operator new(size_t objsize)   {
      return (void*)_SFR_ADDR(RST);        // assign actual memory location of the registers
      }

   };
#endif

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Lots of new's and no delete's ? You sure about that?

 

If you are just going to do one time rather than dynamic allocation/deallocation what's the advantage over static construction?

 

As it stands you can create but not delete.

 

I suppose you are going to say "it happens later"? ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes lots of news and no deletes.  Do you delete your statics?

 

The difference is the constructors run in the proper order when I start up the program.  So you say, yes but my constructors don't do much, except initialize data members (I hope).  Okay, whatever floats your boat.

 

Also it eliminates all those globals.  It seems more modular the way I do it.

 

I like to be able to control what my program contains just by commenting out a new statement.  I can also pass the address of one class to another class easily at startup.

 

For instance I have this line in the startup class for my A3BU Xplained board.

  

      new Parse_in_message(new Usart_e0);

Nobody knows or cares where the Usart_e0 class is except my Parse_in_message class.

 

If I run on another board where the USART is on d0, I just change Usart_e0 to Usart_d0.

 

Maybe some of it depends on how our brains are wired.  Mine used to be wired for C but after a lot of neurological stress, it's now wired for C++.  wink

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Besides the order of construction, there is also inheritance.  I don't know if you can pull that off with statics.

 

Let me give you an example.  I have about 50 different types of messages the PC can send to my AVRs.  In the AVR there are about 50 message handlers.  Each one handles one message type.  So when a message comes in, it has to be routed to the proper handler.

 

I have a message switcher class.  It has to be constructed before the message handlers are constructed.  Each message handler inherits from an instance of the small InBox class.  Each handler knows the type of message it handles.  During construction, it passes the message type to its Inbox class when it is constructed.  The InBox class calls a Register() function in the message switcher class.  It passes its address and the message type it handles.  The message switcher adds this to its table.  

 

When a message comes in, it gets passed to the message switcher.  The switcher gets the type from the message header.  It scans its table to find the address of the InBox that handles that type.  It then calls a Here_is_your_message() function (or something like that) in the InBox class.  Of course the Here_is_your_message function in the InBox is just a virtual function.  The real Here_is_your_message function is in the main (derived) class.

 

I think this is pretty much a typical "registration" procedure for C++ programs.   I think of the message handlers as the "main" classes and the InBox as a cap class.  Technically of course the main class is a derived class and the "cap" class is the base class.  One result of using these "cap" classes is what I think is called polymorphism.  The message switcher only has to know about the InBox class and so only needs to have the header file for that class.  Without polymorphism the message switcher would have to have 50 header files to pass messages to 50 different classes.  And of course when a new message type is added ..... I don't even want to think about it.   Been there and done that.  I think it was called C language programming.  smiley

 

Can you do the same thing in C?   Well maybe.  I suppose you can do it in assembler too.devil

Last Edited: Thu. Jan 8, 2015 - 04:51 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I suppose you can do it in assembler too.

The C or C++ compilers certainly will ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Lots of new's and no delete's ? You sure about that?

 

I suppose you are going to say "it happens later"? ;-)

 

S**t happens!  Intentionally allowing memory leaks is something I can't agree with.  But hey,  I haven't used C++ to program MCUs yet, so who am I to say anything. 

...

steve17 wrote:
Maybe some of it depends on how our brains are wired.  Mine used to be wired for C but after a lot of neurological stress, it's now wired for C++.  wink

Yes, being able to say one is insane does have its advantages.wink

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes lots of news and no deletes.  Do you delete your statics?

If all you are going to do is news and no deletes, I don't see the point of having a special new. As westfw said, it is not the news that are the problem, it is the deletes.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
I don't see the point of having a special new. 
If you mean why not use malloc, I would say size, efficiency and availability. 

 

I suppose malloc is available but actually I don't use any libraries in my programs and I see no reason to take the trouble to find them.

 

I suppose the currently available malloc() is reliable but it wasn't always so.  Why not use mine which is simple and reliable?

 

I don't know how big malloc() is but it must be considerably bigger than my heap allocator.  Mine is 1 function with 3 statements.

 

I figure malloc() must use up some overhead bytes when it allocates.  I guess it allocates more than requested so it can store the size of the block.  Otherwise I don't know how free() could do it's job.  Also malloc must have a mechanism to handle non contiguous free blocks on the heap.   You may not use free() but malloc() doesn't know that.

 

So my heap allocator is simpler.  Indeed very simple.

Last Edited: Thu. Jan 8, 2015 - 08:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

larryvc wrote:

  Intentionally allowing memory leaks

???  What's memory leaks got to do with it?   I new all my stuff at startup.  After that, no more news.  That's good technique for embedded systems of all kinds.

 

That is the way it is done, or should be, when writing an embedded system running on a PC also.  In the case of the PC,  malloc() is used at startup.  After that, no mallocs, no deletes, no heap churning allowed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

malloc() is adequately described here:

http://www.nongnu.org/avr-libc/user-manual/malloc.html

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:

 

larryvc wrote:

 

  Intentionally allowing memory leaks

 

???  What's memory leaks got to do with it?   I new all my stuff at startup.  After that, no more news.  That's good technique for embedded systems of all kinds.

 

 

That is the way it is done, or should be, when writing an embedded system running on a PC also.  In the case of the PC,  malloc() is used at startup.  After that, no mallocs, no deletes, no heap churning allowed.

 

agreed, in an embedded system this is a perfectly valid architecture.

It's not a memory leak if you never delete :)

Keith Vasilakes

Firmware engineer

Minnesota

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I see it was a waste of my time typing in the disclaimer at the end of my twice quoted statement.

"I may make you feel but I can't make you think" - Jethro Tull - Thick As A Brick

"void transmigratus(void) {transmigratus();} // recursio infinitus" - larryvc

"It's much more practical to rely on the processing powers of the real debugger, i.e. the one between the keyboard and chair." - JW wek3

"When you arise in the morning think of what a privilege it is to be alive: to breathe, to think, to enjoy, to love." -  Marcus Aurelius

Last Edited: Fri. Jan 9, 2015 - 08:42 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know how big malloc() is but it must be considerably bigger than my heap allocator.  Mine is 1 function with 3 statements.

00000000 <malloc>:
   0:	cf 93       	push	r28
   2:	df 93       	push	r29
   4:	82 30       	cpi	r24, 0x02	; 2
   6:	91 05       	cpc	r25, r1
   8:	00 f4       	brcc	.+0      	; 0xa <malloc+0xa>
   a:	82 e0       	ldi	r24, 0x02	; 2
   c:	90 e0       	ldi	r25, 0x00	; 0
   e:	e0 91 00 00 	lds	r30, 0x0000
  12:	f0 91 00 00 	lds	r31, 0x0000
  16:	40 e0       	ldi	r20, 0x00	; 0
  18:	50 e0       	ldi	r21, 0x00	; 0
  1a:	20 e0       	ldi	r18, 0x00	; 0
  1c:	30 e0       	ldi	r19, 0x00	; 0
  1e:	00 c0       	rjmp	.+0      	; 0x20 <malloc+0x20>
  20:	60 81       	ld	r22, Z
  22:	71 81       	ldd	r23, Z+1	; 0x01
  24:	68 17       	cp	r22, r24
  26:	79 07       	cpc	r23, r25
  28:	00 f0       	brcs	.+0      	; 0x2a <malloc+0x2a>
  2a:	68 17       	cp	r22, r24
  2c:	79 07       	cpc	r23, r25
  2e:	01 f4       	brne	.+0      	; 0x30 <malloc+0x30>
  30:	82 81       	ldd	r24, Z+2	; 0x02
  32:	93 81       	ldd	r25, Z+3	; 0x03
  34:	21 15       	cp	r18, r1
  36:	31 05       	cpc	r19, r1
  38:	01 f0       	breq	.+0      	; 0x3a <malloc+0x3a>
  3a:	d9 01       	movw	r26, r18
  3c:	13 96       	adiw	r26, 0x03	; 3
  3e:	9c 93       	st	X, r25
  40:	8e 93       	st	-X, r24
  42:	12 97       	sbiw	r26, 0x02	; 2
  44:	00 c0       	rjmp	.+0      	; 0x46 <malloc+0x46>
  46:	90 93 00 00 	sts	0x0000, r25
  4a:	80 93 00 00 	sts	0x0000, r24
  4e:	00 c0       	rjmp	.+0      	; 0x50 <malloc+0x50>
  50:	41 15       	cp	r20, r1
  52:	51 05       	cpc	r21, r1
  54:	01 f0       	breq	.+0      	; 0x56 <malloc+0x56>
  56:	64 17       	cp	r22, r20
  58:	75 07       	cpc	r23, r21
  5a:	00 f4       	brcc	.+0      	; 0x5c <malloc+0x5c>
  5c:	ab 01       	movw	r20, r22
  5e:	e9 01       	movw	r28, r18
  60:	df 01       	movw	r26, r30
  62:	9f 01       	movw	r18, r30
  64:	72 81       	ldd	r23, Z+2	; 0x02
  66:	63 81       	ldd	r22, Z+3	; 0x03
  68:	e7 2f       	mov	r30, r23
  6a:	f6 2f       	mov	r31, r22
  6c:	30 97       	sbiw	r30, 0x00	; 0
  6e:	01 f4       	brne	.+0      	; 0x70 <malloc+0x70>
  70:	41 15       	cp	r20, r1
  72:	51 05       	cpc	r21, r1
  74:	01 f0       	breq	.+0      	; 0x76 <malloc+0x76>
  76:	48 1b       	sub	r20, r24
  78:	59 0b       	sbc	r21, r25
  7a:	44 30       	cpi	r20, 0x04	; 4
  7c:	51 05       	cpc	r21, r1
  7e:	00 f4       	brcc	.+0      	; 0x80 <malloc+0x80>
  80:	12 96       	adiw	r26, 0x02	; 2
  82:	8d 91       	ld	r24, X+
  84:	9c 91       	ld	r25, X
  86:	13 97       	sbiw	r26, 0x03	; 3
  88:	20 97       	sbiw	r28, 0x00	; 0
  8a:	01 f0       	breq	.+0      	; 0x8c <malloc+0x8c>
  8c:	9b 83       	std	Y+3, r25	; 0x03
  8e:	8a 83       	std	Y+2, r24	; 0x02
  90:	00 c0       	rjmp	.+0      	; 0x92 <malloc+0x92>
  92:	90 93 00 00 	sts	0x0000, r25
  96:	80 93 00 00 	sts	0x0000, r24
  9a:	fd 01       	movw	r30, r26
  9c:	32 96       	adiw	r30, 0x02	; 2
  9e:	00 c0       	rjmp	.+0      	; 0xa0 <malloc+0xa0>
  a0:	fd 01       	movw	r30, r26
  a2:	e4 0f       	add	r30, r20
  a4:	f5 1f       	adc	r31, r21
  a6:	81 93       	st	Z+, r24
  a8:	91 93       	st	Z+, r25
  aa:	42 50       	subi	r20, 0x02	; 2
  ac:	50 40       	sbci	r21, 0x00	; 0
  ae:	11 96       	adiw	r26, 0x01	; 1
  b0:	5c 93       	st	X, r21
  b2:	4e 93       	st	-X, r20
  b4:	00 c0       	rjmp	.+0      	; 0xb6 <malloc+0xb6>
  b6:	20 91 00 00 	lds	r18, 0x0000
  ba:	30 91 00 00 	lds	r19, 0x0000
  be:	21 15       	cp	r18, r1
  c0:	31 05       	cpc	r19, r1
  c2:	01 f4       	brne	.+0      	; 0xc4 <malloc+0xc4>
  c4:	20 91 00 00 	lds	r18, 0x0000
  c8:	30 91 00 00 	lds	r19, 0x0000
  cc:	30 93 00 00 	sts	0x0000, r19
  d0:	20 93 00 00 	sts	0x0000, r18
  d4:	20 91 00 00 	lds	r18, 0x0000
  d8:	30 91 00 00 	lds	r19, 0x0000
  dc:	21 15       	cp	r18, r1
  de:	31 05       	cpc	r19, r1
  e0:	01 f4       	brne	.+0      	; 0xe2 <malloc+0xe2>
  e2:	2d b7       	in	r18, 0x3d	; 61
  e4:	3e b7       	in	r19, 0x3e	; 62
  e6:	40 91 00 00 	lds	r20, 0x0000
  ea:	50 91 00 00 	lds	r21, 0x0000
  ee:	24 1b       	sub	r18, r20
  f0:	35 0b       	sbc	r19, r21
  f2:	e0 91 00 00 	lds	r30, 0x0000
  f6:	f0 91 00 00 	lds	r31, 0x0000
  fa:	e2 17       	cp	r30, r18
  fc:	f3 07       	cpc	r31, r19
  fe:	00 f4       	brcc	.+0      	; 0x100 <malloc+0x100>
 100:	2e 1b       	sub	r18, r30
 102:	3f 0b       	sbc	r19, r31
 104:	28 17       	cp	r18, r24
 106:	39 07       	cpc	r19, r25
 108:	00 f0       	brcs	.+0      	; 0x10a <malloc+0x10a>
 10a:	ac 01       	movw	r20, r24
 10c:	4e 5f       	subi	r20, 0xFE	; 254
 10e:	5f 4f       	sbci	r21, 0xFF	; 255
 110:	24 17       	cp	r18, r20
 112:	35 07       	cpc	r19, r21
 114:	00 f0       	brcs	.+0      	; 0x116 <malloc+0x116>
 116:	4e 0f       	add	r20, r30
 118:	5f 1f       	adc	r21, r31
 11a:	50 93 00 00 	sts	0x0000, r21
 11e:	40 93 00 00 	sts	0x0000, r20
 122:	81 93       	st	Z+, r24
 124:	91 93       	st	Z+, r25
 126:	00 c0       	rjmp	.+0      	; 0x128 <malloc+0x128>
 128:	e0 e0       	ldi	r30, 0x00	; 0
 12a:	f0 e0       	ldi	r31, 0x00	; 0
 12c:	cf 01       	movw	r24, r30
 12e:	df 91       	pop	r29
 130:	cf 91       	pop	r28
 132:	08 95       	ret

0x132 = 306 bytes. This is the one from libc.a for avr5 architecture in 4.5.3 (yeah, I know!).

 

Source (of latest) is here:

 

http://svn.savannah.nongnu.org/v...

Last Edited: Fri. Jan 9, 2015 - 09:19 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:

New calls whatever you want it to call.  It's only requirement is that it return the address of the object you new.   New is good.  Much better than static classes.   malloc() has little or no use in embedded code.

 

If you want to allocate from the heap and later return it to the heap, you need malloc() and free().  You also need your head examined if you are writing embedded code.

 

Can't agree.

small RAM micro.

Static classes - for embedded where you KNOW that the static instance is needed - is smart.  The only issue I have with this is if you put too much code in the constructors that have no way to do error recovery.

embedded systems - small micro RAM, I say never use free(), and if you use malloc(), don't free() that RAM.

There are some BAD implementations of malloc(). Some don't attempt to coalesce.

Most small MCU libraries I've seen use some form of malloc() for New.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When does the program construct the 50-odd message handlers?

Last Edited: Fri. Jan 16, 2015 - 12:53 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pblase wrote:

When does the program construct the 50-odd message handlers?

At startup.  Everything is constructed at startup.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm looking at doing something similar, and was just wondering if you had a clean way of doing all of those initializations, or if there's just 50 "new" calls in a row.

Thanks

Paul

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:

...   I new all my stuff at startup.  After that, no more news.  That's good technique ...

 

<can't resist; can't resist; ...>  Aah, I get it -- no news is good news.

 

Full disclosure:  I'm not a C++ person.  But Johan and others have pounded into us mere bit-pushers that C++ on an AVR doesn't have to be a bloated pig.  I can't recall exactly, but I think there were three guidelines?  And one of them is along the lines of what steve17 described.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Using new/delete on an embedded device as you would in a PC program has more impact on memory management rather than code bloat, though there is typically some overhead using new.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pblase wrote:

there's just 50 "new" calls in a row.

Paul

Yeah, that's basically it.  I do have #ifdef stuff around each one.  I have an options.h file in the "home" folder of my projects so I could select which ones I want.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, thanks much.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:
I have a message switcher class.  It has to be constructed before the message handlers are constructed.  Each message handler inherits from an instance of the small InBox class.  Each handler knows the type of message it handles.  During construction, it passes the message type to its Inbox class when it is constructed.  The InBox class calls a Register() function in the message switcher class.  It passes its address and the message type it handles.  The message switcher adds this to its table.
Classes inherit from other classes, not from objects.

In the case at hand, I'd think that an array of function pointers would be best.

If you still think that an array of pointers to object/type-number pairs is best:

class Base { ... } ;
class D1 : public Base { ... } ;
...
class D50 : public Base { ... } ;

struct Twosome { int typ; Base *ptr; } ;

D1 d1(...);
...
D50 d50(...);

Twosome array [] = {
    1, &d1,
    2, &d2,
    4, &d3,
    3, &d4,
   56, &d5,
   ...
   9, &d50
} ;

 

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That sounds like the visitor pattern to me

Keith Vasilakes

Firmware engineer

Minnesota

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

During construction, it passes the message type to its Inbox class when it is constructed.  The InBox class calls a Register() function in the message switcher class.  It passes its address and the message type it handles.  The message switcher adds this to its table. 

 

So the message switcher is building the table dynamically during startup? How do you size the table initially? I can see how this could either chew up or confuse the heap during startup on an embedded system.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pblase wrote:

 I can see how this could either chew up or confuse the heap during startup on an embedded system.

I don't understand.  How could it confuse the heap?  You allocate from the heap with new.  It knows what to do. 

 

I didn't remember how I did it, so I looked.  The switcher uses a fixed size table.  If the table is full, it doesn't add any more entries.   When a message with unknown type (not in the table) is received, the switcher sends an error message back to the sender.

 

There are several ways you could do it.

 

You could do message types this way:

enum   {

   A_msg_type,

   Another_msg_type,

   Yet_another_msg_type,

   .

   .

   .

   One_more_msg_type,

   LAST_MSG_TYPE,

   };

 

This would number the types from zero.   If you always had LAST_MSG_TYPE at the end, the message switcher could use this to size the table.  With this method, the message type could be used as an index to fetch the InBox pointer.

 

 

You could use a linked list and new a list member for each msg type.  This would use more memory than other methods.

Last Edited: Sat. Jan 17, 2015 - 10:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ah. Ok, that makes sense. Thanks much.

Paul

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Question: if I redefine "new()" like this, or otherwise use malloc to allocate memory for a new class, how does the "this" pointer get set?

What I would like is to have an array of data structs, with one of the elements being a (void *) labeled "additionalProcessing". When the array is initialized, additionalProcessing is set to an object that handles particular elements in the data struct. Normally I'd create the additionalProcessing object with new, but the AVR GCC new function is not implemented. Any suggestions gratefully welcomed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

pblase wrote:
if I redefine "new()" like this, or otherwise use malloc to allocate memory for a new class, how does the "this" pointer get set?

Huh? Whether you use a stock new() or override to implement your own it returns the address to the new object. Whenever you use the object to call one of the member functions of the class the compiler is responsible for tacking on the this pointer as the (hidden) first parameter to the method.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]