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.
Questions on the RTC lib code DateTime class
Moderators: adafruit_support_bill, adafruit
Please be positive and constructive with your questions and comments.
- kscharf
- Posts: 277
- Joined: Wed Sep 10, 2008 10:29 am
- adafruit_support_mike
- Posts: 67485
- Joined: Thu Feb 11, 2010 2:51 pm
Re: Questions on the RTC lib code DateTime class
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.
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.
- kscharf
- Posts: 277
- Joined: Wed Sep 10, 2008 10:29 am
Re: Questions on the RTC lib code DateTime class
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.
Datetime is not a very complicated class, but the implemtation felt 'uncomfortable' to me.
- adafruit_support_mike
- Posts: 67485
- Joined: Thu Feb 11, 2010 2:51 pm
Re: Questions on the RTC lib code DateTime class
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."
-
- Posts: 564
- Joined: Wed Jun 19, 2013 3:35 am
Re: Questions on the RTC lib code DateTime class
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 -
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 -
As, this by itself wouldn't produce anything very helpful -
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
And, it is used as a type of an object in RTC_DS1307 class
…and, it's static.
Looking at now() -
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?
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();
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;
Code: Select all
rtc.now();
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);
Code: Select all
class RTC_DS1307 {
public:
static DateTime now();
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);
}
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?
- adafruit_support_mike
- Posts: 67485
- Joined: Thu Feb 11, 2010 2:51 pm
Re: Questions on the RTC lib code DateTime class
Exactly correct. In CS speak, classes are called 'abstract data types'.1chicagodave wrote:Objects & variables… Classes are to objects as types are to variables. And, they are respectively extremely similar in some cases.
Yes.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'?
Conceptually RTC.now() returns an object. In terms of the actual memory allocation, it looks like this:
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".
Generally speaking, that would do the steps for the green and grey arrows, but not the red arrows.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….)?
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.
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.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?
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.
-
- Posts: 564
- Joined: Wed Jun 19, 2013 3:35 am
Re: Questions on the RTC lib code DateTime class
Thank you for understanding why I had to ask and not stopping at:
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!
...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!
- adafruit_support_mike
- Posts: 67485
- Joined: Thu Feb 11, 2010 2:51 pm
Re: Questions on the RTC lib code DateTime class
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.
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.
- kscharf
- Posts: 277
- Joined: Wed Sep 10, 2008 10:29 am
Re: Questions on the RTC lib code DateTime class
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.
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.
-
- Posts: 1
- Joined: Wed Mar 19, 2014 4:29 am
Re: Questions on the RTC lib code DateTime class
Thanks for this information . I am also trying this on my blog Time Education . Again thanks for nice post
Please be positive and constructive with your questions and comments.