Questions on the RTC lib code DateTime class

Post here about your Arduino projects, get help - for Adafruit customers!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
kscharf
 
Posts: 277
Joined: Wed Sep 10, 2008 10:29 am

Questions on the RTC lib code DateTime class

Post by kscharf »

Both the libraries for the DS3231 (cronodot) and the DS1307 use the RTClib Datetime class.
They return the time by calling the constuctor of the datetime class, and return an instance of the class.
This sounds like a bad practice in embedded code to return a class object. I'm not sure how GCC handles this, it may return the class by registers, but more likely it is a stack object. The danger is in what happens to the allocated memory AFTER the calling code finishes extracting the data from the class. Eventually, we could end up with a stack overflow if the calling function (such as loop()) never goes out of scope and the destructor of the datatime class is never called. We could end up with a stack full of datetime objects.

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Questions on the RTC lib code DateTime class

Post by adafruit_support_mike »

Short version: you only have to worry about memory leaks in C++ if you create objects by calling 'new' and don't call 'delete' when you want to get rid of them.

C and C++ are 'call by value' languages. Space in the stack is called a 'stack frame' and is allocated just before a function comes into scope. It contains enough room for the input parameters, return value, and any local variables declared in the function. The values of the input parameters are copied from locations in the caller's stack frame to locations in the callee's stack frame, then execution enters the function. When the function goes out of scope, the return value is copied from the appropriate location in the callee's stack frame to the caller's stack frame, then the callee's entire stack frame is deleted. Yes, compilers optimize the process by using registers for the input and output parameters, but that's a shortcut which doesn't violate any isolation associated with the call-by-value discipline.

When a C++ function returns an object, technically it returns a pointer to the object and the class's 'copy constructor' is called to copy all the values associated with the object in the callee's stack frame to equivalent positions in the caller's stack frame. Compilers are /allowed/ to use a trick called "named return value optimization" to use the same chunk of actual memory for both objects and skip the copying, but again, that has to maintain the illusion of being a simple call-by-value operation.

Objects are called 'trivial' if they only contain direct data values.. ints, floats, etc.. no pointers to memory outside the object itself. For trivial classes, the compiler will generate a constructor, destructor, and copy constructor automatically because trivial objects are basically larger versions of local variables.

The constructor for any scoped object is called when the block containing the object's declaration comes into scope, and the destructor is called when the block goes out of scope. In the case of object return values, the copy constructor is called to move values from the callee's stack frame to the caller's stack frame.

So.. for scoped objects, it's impossible to have a memory leak of the kind you described. The memory for the object created as `DateTime::now()`'s return value is allocated when execution enters the function, and deleted when execution leaves the function. An object in `loop()` that receives values returned by `DateTime::now()` never changes size, you just keep copying new values into the same locations.

'Dynamic' objects are allocated in the heap, and the only scoped value in a function's stack frame is a pointer to the block of memory in the heap. You create dynamic objects by using the `new` keyword. To release the memory in the heap, you have to use the `delete` keyword. If you use `new`, then fail to call `delete` when the last pointer to the object goes out of scope, you get a memory leak.

Memory leaks are only possible for objects created with the `new` keyword though.

User avatar
kscharf
 
Posts: 277
Joined: Wed Sep 10, 2008 10:29 am

Re: Questions on the RTC lib code DateTime class

Post by kscharf »

Thanks for the explanation. I was well aware of how the stack is used to pass by value INTO a function. I am also aware of how scalar values are returned from a function (in the avr this is done through registers). However, I was uncertain how this would happen when a structure is returned. In most of the professional programming I've done structures and classes were always returned by reference or pointer, so that the memory used was under my control, and memory leaks were avoided.
Datetime is not a very complicated class, but the implemtation felt 'uncomfortable' to me.

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Questions on the RTC lib code DateTime class

Post by adafruit_support_mike »

Yeah, the mechanics of object lifespan and memory allocation are under-the-hood things a high-level language should theoretically hide. C has been described as "a high level language that combines the power of assembly language with the readability and maintainability of assembly language." ;-)

1chicagodave
 
Posts: 564
Joined: Wed Jun 19, 2013 3:35 am

Re: Questions on the RTC lib code DateTime class

Post by 1chicagodave »

If you guys couldn't mind humoring me, I'd like to explore this discussion topic a bit further…?

I've been teaching myself C++, and I recently spent a few nights using this RTClib to try and understand a few new concepts. The OP has me questioning my recent understanding of how this code works.

What I thought I understood -
Objects & variables… Classes are to objects as types are to variables. And, they are respectively extremely similar in some cases.

The key to how I understood this code & library is how it is used in this line of code -

Code: Select all

    DateTime now = rtc.now();
Which looks like it's initializing a DateTime object called now using variable initialization syntax.
Since there is no default constructor for the DateTime class, the following line of code by itself wouldn't be valid -

Code: Select all

 DateTime now;
As, this by itself wouldn't produce anything very helpful -

Code: Select all

rtc.now();
Instead, we pass in the parameters for a valid constructor from the RTC.now() using the assignment operator.

I guess the distinction I want to make sure I understand (purely for my own education) is -
Does RTC.now() actually return an instance of an object? OR, do we just use it's output to help us create the DateTime object 'now'?
If we just ran the function rtc.now(); by itself, would it do anything other than read the current time & update its variables (ss, mm, hh….)?

That's about it. I wrote the rest of this while I was trying to work out what my question actually was. It does include the relevant code, though...if that helps. - Thanks!

Of the numerous appearances of 'DateTime' in the RTClib, it is a class with multiple constructors

Code: Select all

class DateTime {
public:
    DateTime (uint32_t t =0);
    DateTime (uint16_t year, uint8_t month, uint8_t day,
                uint8_t hour =0, uint8_t min =0, uint8_t sec =0);
    DateTime (const char* date, const char* time);
And, it is used as a type of an object in RTC_DS1307 class

Code: Select all

class RTC_DS1307 {
public:
    static DateTime now();
…and, it's static.

Looking at now() -

Code: Select all

DateTime RTC_DS1307::now() {
  WIRE.beginTransmission(DS1307_ADDRESS);
  WIRE.write(0);        
  WIRE.endTransmission();

  WIRE.requestFrom(DS1307_ADDRESS, 7);
  uint8_t ss = bcd2bin(WIRE.read() & 0x7F);
  uint8_t mm = bcd2bin(WIRE.read());
  uint8_t hh = bcd2bin(WIRE.read());
  WIRE.read();
  uint8_t d = bcd2bin(WIRE.read());
  uint8_t m = bcd2bin(WIRE.read());
  uint16_t y = bcd2bin(WIRE.read()) + 2000;
  
  return DateTime (y, m, d, hh, mm, ss);
}
It assigns/updates a bunch of variables which we then need to get from rtc to now so we can call them using now.hours(), now.minutes(), and so on.
I think of how functions can return a variable…but, if it's not explicitly assigned to anything it just kind of disappears; it as little effect on anything (that I know of). Does this return act differently?

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Questions on the RTC lib code DateTime class

Post by adafruit_support_mike »

1chicagodave wrote:Objects & variables… Classes are to objects as types are to variables. And, they are respectively extremely similar in some cases.
Exactly correct. In CS speak, classes are called 'abstract data types'.
1chicagodave wrote:Does RTC.now() actually return an instance of an object? OR, do we just use it's output to help us create the DateTime object 'now'?
Yes. ;-)

Conceptually RTC.now() returns an object. In terms of the actual memory allocation, it looks like this:
stack frames.jpg
stack frames.jpg (32.95 KiB) Viewed 819 times
Values from the calling code's stack frame are copied into the callee's stack frame. The callee copies those values into an object. The values in the memory slots for the callee's object get copied to memory slots in the caller's object by the code that handles return values.

The question of whether two objects whose properties all hold the same values are "the same" is mostly philosophical. Call-by-value languages make things simple by saying "no" and declaring any example that makes the answer "yes" a violation of call-by-value semantics. It's a critically important question when you're dealing with memory allocation though, so 'object identity' is its own whole (complex) subject.

For call-by-value semantics, your question is on the "you shouldn't care, so don't ask" list. You have to ask it to truly grok the lanugage though, so the correct answer, taken from Zen, is "mu". ;-)
1chicagodave wrote:If we just ran the function rtc.now(); by itself, would it do anything other than read the current time & update its variables (ss, mm, hh….)?
Generally speaking, that would do the steps for the green and grey arrows, but not the red arrows.

In practice, the compiler knows how to track values and operations necessary to create a return value, or which have some effect visible outside the scope of the function where the operations happen. The optimizer uses that information to decide whether operations can be simplified or removed entirely. In this case, a good optimizer would see that the function call doesn't do anything worth remembering, and would probably remove the whole call.
1chicagodave wrote:I think of how functions can return a variable…but, if it's not explicitly assigned to anything it just kind of disappears; it as little effect on anything (that I know of). Does this return act differently?
Nope. In terms of value-copying behavior, the compiler sees a scoped object as a multi-byte data structure only slightly more complicated than a uint32_t.

The compiler's parser will create a tree data structure that has a node for every statement in the source file. The lexical analyzer will then walk the parse tree and create a data structure called the 'abstract syntax tree' which is logically equivalent to the parse tree but tidier. If you tell the lexical analyzer to optimize the code, it will remove branches that don't do anything useful. Then the code generator walks through the abstract syntax tree creating machine code that's logically equivalent to that.

1chicagodave
 
Posts: 564
Joined: Wed Jun 19, 2013 3:35 am

Re: Questions on the RTC lib code DateTime class

Post by 1chicagodave »

Thank you for understanding why I had to ask and not stopping at:
...your question is on the "you shouldn't care, so don't ask" list.
:?

There are a lot of great resources out there, but my vocabulary is still limited enough that it's difficult to narrow my scope and find the right information.

Your explanation was excellent, and the sketch was great. It helped me stumble on some interesting stuff on copy, swap, move, left-hand, right-hand, reference, implicit.... Giving me good ideas on how to improve some things I've been thinking about.

Thanks again, Mike!

User avatar
adafruit_support_mike
 
Posts: 67485
Joined: Thu Feb 11, 2010 2:51 pm

Re: Questions on the RTC lib code DateTime class

Post by adafruit_support_mike »

Glad to hear it was useful.

Fair warning: computing theory scoffs at questions that are merely 'deep'. It's a (provably) bottomless pit, and the farther you dive into it the more you have to start using things like Zen koans to express the ideas.

If you want to pick up a foundation, find a university library and skim through a few textbooks on "Theory of Computation". Don't sweat the proofs and 'pumping lemma' stuff, but get a feel for the relationships between regular expressions and state machines, context-free grammars and stacks, random-access memory and Turing machines. That's the coat-rack all the other concepts ultimately hang from.

For useful programming, get a copy of _Introduction to Algorithms_ by Cormen and Rivest. It's pretty much the definitive work on the subject.

To learn the beauty of programming, read everything you can find by Don Knuth. His six-volume set, _The Art of Computer Programming_ is *the* classic work in the field, and we all hope he lives long enough to finish it (he's currently up to volume 4). Knuth is also the reason there are so few squabbles over pecking order among programmers.. if you aren't Don Knuth, there's at least one person who knows a hell of a lot more about programming than you.

Also read _The Structure and Intepretation of Computer Programs_ by Abelson and Sussman^2: http://mitpress.mit.edu/sicp/full-text/book/book.html Yes, you have to learn LISP to work through it, but it's been said that any sufficiently complex program eventually becomes a slow, buggy approximation of a LISP interpreter, so it helps to know where you're heading.

User avatar
kscharf
 
Posts: 277
Joined: Wed Sep 10, 2008 10:29 am

Re: Questions on the RTC lib code DateTime class

Post by kscharf »

BTW, the compilier must be providing a default constructor for DateTime since
DateTime Now, Then;
actually DOES work!

I can then use the copy / assignement operator to do
Now = myRtc.now();
to get the current time.

I've tested this, and it does work in the project I'm building.

mchopra62
 
Posts: 1
Joined: Wed Mar 19, 2014 4:29 am

Re: Questions on the RTC lib code DateTime class

Post by mchopra62 »

Thanks for this information . I am also trying this on my blog Time Education . Again thanks for nice post

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino”