ATmega programming using Arduino Library - #2 [continuing]

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

 

This is a very similar question to the other "ATmega programming ..." but with a markedly different emphasis. 

 

Suppose that I have an Arduino library written for a specific add-on device. In this case, it is LoRa radio chip. It uses a C++ class to interface to the radio. I would like  to incorporate this code into a standard C program (for Mega328P). Rub is, I know ALMOST nothing about C++!

 

I plan to provide substitute functions for the accesses to the MCU IO pins. Still called DigitalWrite() and such, but hand crafted to be MCU specific and much tighter. Likewise, I plan to provide substitute functions ("wrappers", I guess) to call the AVR LibC delay functions instead of the Arduino ones. Looks like some substitute serial functions will also be needed. I think those are the three big interface issues.

 

What I am much less clear about is how to make this library/class part of my program. I think that I need to call the class "constructor" somehow, but not sure how to do that or anything else to get it started. Nor, am I clear on calling functions within that class from the "regular C" part of the program.

 

So, if some expert on such matters could provide a little guidance on the details of incorporating an Arduino interface library into a non-Arduino C program, I would appreciate it VERY much!

 

Thanks

Jim

This topic has a solution.

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Jun 19, 2017 - 07:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Constructor is basically "init" so if you have a class "Foo" then just copy the constructor code into a Foo_init() and call that first. You may find that you have to change the member initialization stuff though.
.
BTW why not turn this round and simply make main.c into main.cpp and have the whole program as C++?

Last Edited: Sat. Jun 17, 2017 - 07:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What would starting out with main.cpp do? What are the ramifications? Especially for someone who has never knowingly coded in C++?

 

JIm

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Apart from stronger type checking a C program renamed to be C++ should just compile and run as before but now you can instantiate C++ classes in it too (and do all the other stuff C++ then offers) but the point is that apart from accessing classes you dont HAVE to use any of the new stuff.

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

So why don't we generally use main.cpp rather than main.c?

 

My big question, though: how does one "instantiate" a class? I see code like:

RH_RF95 rf95;

Is that the instantiate statement?

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know why people don't choose C++ (though Arduino sensibly did!)
.
And yes that's an instantiation of a class. Just think of a class a bit like a typedef struct.

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

Appreciate your help!

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

When going to C++, one thing to be aware of is 'name mangling'. In order to avoid clashes of the same name in different objects, the compiler 'mangles' the names. Normally this is not an issue, but if you have external references to C things, you need to know of extern "C" that tells the compiler the name is not mangled. Interrupt vectors always seem to catch me out.

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

Can you elaborate on what the programmer needs to do?

 

Thanks,

Jim

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Extern "C"

Google C++ name mangling
For a more complete treatment of the subject.

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

You have a main.cpp with C++ code. You have some old but good and proven C-functions in sub.c. There is an accompanying header file called sub.h. You want to call the C-functions from the C++ code. You tell the C++ compiler that is should look for functions using the C-convention for symbol identification by doing

// main.cpp

extern "C" {
#include "sub.h""
}

That's it.

 

After a while you get tired writing that into every .cpp file you call C-code from, and you figure that you can make the header files agnostic to whether they are #included from C or C++:

 

//sub.h

#ifdef __cplusplus
extern "C" {
#endif

int foo(double bar);
void baz();

#ifdef __cplusplus
}
#endif

With the above technique, if this header file is seen by the C++ compiler it will see the prototypes surrounded by the required extern "C" since the __cplusplus preprocessor symbol is always defined when the C++ compiler runs. If, on the other hand, a C compiler sees this file it will  not see the extern "C" since _cplusplus is not defined when a C compiler runs. Now your header files are C/C++ agnostic!

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

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

Thank you Johan! Big help.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

But if you stick to all C++ you don't need worry. Even if you use some established C, perhaps Fleury for I2C or LCD then you should be able to just rename those c to cpp

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

Is that all it takes? Is there anything that you can do in a macro or other way to make that automatic?

 

Thanks

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

The common method is what Johan described. Looking at the arduino core code should answer most of your queries on using c++

Last Edited: Sun. Jun 18, 2017 - 11:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Had not thought of that as a learning tool. Good idea. 

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

The recurring question we get here is how to handle interrupts in C++. As the vector points to a function, you can't have it call a member function of an object. If you look at the Arduino core class for serial you'll see how they handle interrupts. You'll see the application of extern "C" here. I've just been through the process on another platform.

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

Thanks -

 

Have not run into that yet, though I am sure I will. Knowing almost nothing about C++, I didn't know that would be an issue.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Jun 19, 2017 - 04:38 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Does a C++ program use the same io.h file and same libc headers? Do I have to use the same extern C mechanism with these? If I read Johan's notes correctly, you would need to apply the same

#ifdef __cplusplus

 

mechanism. But, these headers already  have the prototypes, don't they? So, would the headers need to be rewritten? 

 

Thanks for putting up with my uninformed questions!

Jim

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

It's all been done for you. You can write your code as you've been used to. Take the plunge!

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

Kartman wrote:
It's all been done for you.
+1

C:\SysGCC\avr\avr\include>grep -H cplusplus * -r
assert.h:#ifdef __cplusplus
assert.h:#ifdef __cplusplus
avr/eeprom.h:#ifdef __cplusplus
avr/eeprom.h:#ifdef __cplusplus
avr/interrupt.h:#ifdef __cplusplus
avr/interrupt.h:#ifdef __cplusplus
avr/interrupt.h:#ifdef __cplusplus
avr/interrupt.h:#ifdef __cplusplus
avr/interrupt.h:#else     /* !__cplusplus */
avr/interrupt.h:#endif  /* __cplusplus */
avr/pgmspace.h:#ifdef __cplusplus
avr/pgmspace.h:#ifdef __cplusplus
compat/deprecated.h:#ifdef __cplusplus
ctype.h:#ifdef __cplusplus
ctype.h:#ifdef __cplusplus
errno.h:#ifdef __cplusplus
errno.h:#ifdef __cplusplus
inttypes.h:#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS)
inttypes.h:#endif       /* !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) */
math.h:#ifdef __cplusplus
math.h:#ifdef __cplusplus
setjmp.h:#ifdef __cplusplus
setjmp.h:#ifdef __cplusplus
stdint.h:#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS)
stdint.h:#endif /* !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) */
stdint.h:#if (!defined __cplusplus || __cplusplus >= 201103L \
stdint.h:#endif /* (!defined __cplusplus || __cplusplus >= 201103L \
stdio.h:#ifdef __cplusplus
stdio.h:#ifdef __cplusplus
stdlib.h:#ifdef __cplusplus
stdlib.h:#ifdef __cplusplus
string.h:#ifdef __cplusplus
string.h:#ifdef __cplusplus
time.h:#ifdef __cplusplus
time.h:#ifdef __cplusplus
util/eu_dst.h:#ifdef __cplusplus
util/eu_dst.h:#ifdef __cplusplus
util/usa_dst.h:#ifdef __cplusplus
util/usa_dst.h:#ifdef __cplusplus 

AVR-LibC has always been written to support both C and C++.

 

(note it does require you to use things like <string.h>, the <cstring> equivalent is not available)

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

BTW regarding #13 I just tried an experiment. I got a copy of the Fleury LCD code and just took lcd.c, lcd.h and test_lcd.c from it. I then built in C as follows:

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -Os -DF_CPU=1000000UL test_lcd.c lcd.c -o lcd.elf

C:\SysGCC\avr\bin>avr-nm lcd.elf | grep " T"
000000d2 T __bad_interrupt
000000a8 T __ctors_end
000000a8 T __ctors_start
000000b4 T __do_copy_data
000000a8 T __dtors_end
000000a8 T __dtors_start
00000370 T __itoa_ncheck
00000054 T __trampolines_end
00000054 T __trampolines_start
00000388 T __utoa_common
00000386 T __utoa_ncheck
00000000 T __vectors
000003de T _etext
000003da T _exit
000001ba T lcd_clrscr
00000180 T lcd_command
00000192 T lcd_data
000001b2 T lcd_getxy
000001a4 T lcd_gotoxy
000001c0 T lcd_home
00000224 T lcd_init
000001c6 T lcd_putc
000001ee T lcd_puts
00000206 T lcd_puts_p
00000292 T main
000003ba T strrev
000000d6 T wait_until_key_pressed

That built the code with no warnings/errors as C code. The avr-nm output shows the functions that were created with their expected "C like" names.

 

If I do nothing more than rename the files and rebuild as C++ I get:

C:\SysGCC\avr\bin>ren lcd.c lcd.cpp

C:\SysGCC\avr\bin>ren test_lcd.c test_lcd.cpp

C:\SysGCC\avr\bin>avr-gcc -mmcu=atmega16 -Os -DF_CPU=1000000UL test_lcd.cpp lcd.cpp -o lcd.elf

C:\SysGCC\avr\bin>avr-nm lcd.elf | grep " T"
000000d2 T __bad_interrupt
000000a8 T __ctors_end
000000a8 T __ctors_start
000000b4 T __do_copy_data
000000a8 T __dtors_end
000000a8 T __dtors_start
00000370 T __itoa_ncheck
00000054 T __trampolines_end
00000054 T __trampolines_start
00000388 T __utoa_common
00000386 T __utoa_ncheck
00000000 T __vectors
000003de T _etext
000003da T _exit
000001ba T _Z10lcd_clrscrv
000001a4 T _Z10lcd_gotoxyhh
00000206 T _Z10lcd_puts_pPKc
00000180 T _Z11lcd_commandh
000000d6 T _Z22wait_until_key_pressedv
00000192 T _Z8lcd_datah
000001c0 T _Z8lcd_homev
00000224 T _Z8lcd_inith
000001c6 T _Z8lcd_putcc
000001ee T _Z8lcd_putsPKc
000001b2 T _Z9lcd_getxyv
00000292 T main
000003ba T strrev

Once again it builds without errors but it is clearly build as C++ code as the function names are now "mangled" so that something like:

000001a4 T lcd_gotoxy

has become:

000001a4 T _Z10lcd_gotoxyhh

The 10 is the number of characters in the "real" name (up to 'y') so the "hh" on the end are added to shows the types of parameters. Those h say this is a version of lcd_gotoxy() that takes two uint8_t as parameters.

 

Oh and as a further bit of interest. This is the size of the C code:

C:\SysGCC\avr\bin>avr-size lcd.elf
   text    data     bss     dec     hex filename
    990      42       0    1032     408 lcd.elf

(so 990 bytes of code).

And this is the size of the C++ version:

C:\SysGCC\avr\bin>avr-size lcd.elf
   text    data     bss     dec     hex filename
    990      42       0    1032     408 lcd.elf

So those who would say "ah but C++ is large and bloaty compared to C" have once again been proven to have unfounded concerns. ;-)

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

You just wait, Cliff. Soeone will come along with a splendidly ignorant argument..

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

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

I really do appreciate this input. I WILL be trying it very shortly. Spending a big chunk of today reviewing Arduino.

 

Cheers, and thanks, again!

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

One of these days I will have a tricky electronics question, and it's payday Jim! ;-)
Consider that joke a fine compliment!

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

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

I will certainly help with electronics questions you might have. You have earned it! Several times over!

 

For anyone who might be reading - this topic would certainly benefit from a well-crafted tutorial that includes a template for a main.cpp that includes ISR examples and examples of working with libraries that have not been adapted to be C/C++ friendly.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Jun 19, 2017 - 05:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ka7ehk wrote:
For anyone who might be reading - this topic would certainly benefit from a well-crafted tutorial that includes a template for a main.cpp that includes ISR examples and examples of working with libraries that have not been adapted to be C/C++ friendly.
As others have said the Arduino serial stuff is probably the "template" most people look at when trying to determine how to do this kind of thing.

 

The class it uses is:

 

https://github.com/arduino/Ardui...

 

with the implementation of (almost) all that here:

 

https://github.com/arduino/Ardui...

 

but then for each potential serial port (up to 4) there is a separate file such as:

 

https://github.com/arduino/Ardui...

 

and not only does that actually instantiate the class:

 

#if defined(UBRRH) && defined(UBRRL)
  HardwareSerial Serial(&UBRRH, &UBRRL, &UCSRA, &UCSRB, &UCSRC, &UDR);
#else
  HardwareSerial Serial(&UBRR0H, &UBRR0L, &UCSR0A, &UCSR0B, &UCSR0C, &UDR0);
#endif

so that "Serial" is a live copy of the "HardwareSerial" stuff for the first/only UART but that second implementation file also has:

#if defined(USART_RX_vect)
  ISR(USART_RX_vect)
#elif defined(USART0_RX_vect)
  ISR(USART0_RX_vect)
#elif defined(USART_RXC_vect)
  ISR(USART_RXC_vect) // ATmega8
#else
  #error "Don't know what the Data Received vector is called for Serial"
#endif
  {
    Serial._rx_complete_irq();
}

So that makes a call to the copy of the rx_complete_irq() that is in the copy of the HardwareSerial class that has been instantiated as "Serial". The two functions called from the ISRs are held in:

 

https://github.com/arduino/Ardui...

 

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

Valuable stuff, Cliff. 

 

What is this "virtual" all about?

 

virtual int available(void);
virtual int peek(void);
virtual int read(void);

 

And I don't see any reference to avr/io.h. How does it assign interrupt vector locations? In other words, do I have to have something else besides avr/io.h?

 

More appreciation!

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Jun 19, 2017 - 05:27 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I need to switch from the pad to a proper laptop to deal with 'virtual'. Meanwhile you take a deep breath, Jim.. You are now entering object-oriented heart-land..

Hang in there. It's going to be a substantial piece of text...

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

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

I am really happy to see this stuff all in one thread. Pretty sure others will find it useful.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

I wouldn't (at first) try to learn more C++ than you have to.

 

"virtual" is about different variants of objects derived from a common base that has some things they share. The "virtual" basically means (and yes I know C++ purists this is an over-simplification) "each object type derived from this base needs to provide their own version of this functionality". The classic example is "shape" as the base and "rectangle" and "triangle" as the derived objects and each provides their own "area" function where the rectangle version is simply width * height while the triangle version is more complex.

 

But you can write a lot of C++ before any of that stuff starts to matter ;-)

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

OK, Cliff -

 

That is as much as I need to know, probably. 

 

On interrupts, my "classic" program structure stacks the interrupts for the whole project at the top of the main.c module (well, after the #includes and the #defines and the variable declarations), even if I have a separate serial.c to implement serial code. Now, I do understand that it might be cleaner, code-wise, to have the interrupt(s) all together with the functions and operations it might be related to. 

 

In the Arduino code you you referenced, that all seems to be bundled together in a class. Is this just the coder's preference or enhanced "code reuse" or is there some underlying "big reason" for doing this?

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Jun 19, 2017 - 05:50 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First and foremost: Cliffs note above is (largely) correct [1]. You can write a lot of C++ without having to use virtual. But if you want to read C++ code, e.g. some of the Arduino "libraries", then it helps to have some understanding of the concept.

 

Of-course I just had to say "largely correct". After all - I am a C++ purist. ;-) 

 

With that I start

 

The ridiculously short introduction to object-oriented programming in C++

Part 1 - a C example

 

Luckily, we can not get to virtual without dealing with a few other things first. I say "luckily" because this will mean we cover all of the "basics of object orientation" which is classically covered by these three words: Encapsulation, Inheritance, Polymorphism.

 

To deal with encapsulation I'd like to start with some plain C. I will also steal Cliffs excellent example with the "shapes" and calculation of their areas.

 

Plain C:

typedef struct {
    int radius;
    double area;
} circle_t;

double circle_compute_area(circle_t circle) {
    circle.area = circle.radius * 2 * 3.1415;
}

void circle_print_area(circle_t circle) {
    printf("Area is %f", circle.area);
}

typedef struct {
    int side;
    double area;
} square_t;

double square_compute_area(square_t) {
    square.area = square.side * square.side;
}

void square_print_area(square_t square) {
    printf("Area is %f", square.area);
}

int main(int argc, char * argv[]) {
    circle my_circle;
    my_circle.radius = 7;
    circle_compute_area(my_circle);
    circle_print_area(my_circle);

    // And something similar for a square not typed in to save
    // lines and fingertips..
}

So, we have the possibility to create both circles and squares, and to compute their areas and to get them printed out. I hope there's nothing strange here (unless I plated a big typo...).

 

The code above is

i) Untested. I'm just typing (I just realized I had no x86 or i64-targeting C++ compiler on this machine...)

ii) Incomplete when it comes to stuff not important for the sublect (i.e. no #included header files etc)

 

So as not to lose what I'm typing I will post this now. Each subsequent step will be in a separate post. Go make a cup of tea or coffee now..

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Mon. Jun 19, 2017 - 06:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Johan -

 

You are a Prince! Maybe even a King! I will carefully review what you write.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

JohanEkdahl wrote:
You just wait, Cliff. Soeone will come along with a splendidly ignorant argument..

 

// 'Hello World!' program 
 
#include <iostream>
 
int main()
{
  std::cout << "Hello World!" << std::endl;
  return 0;
}

 

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

deleted (keeps  the best part of topic consistent)

Last Edited: Mon. Jun 19, 2017 - 06:37 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The ridiculously short introduction to object-oriented programming in C++

Part 2 - C++ Encapsulation

 

Encapsulation is the first pillar (or "tier") of object orientd programming. It has two aspects:

 

- Keeping things together (as an "object")

 

- Keeping the inner state of the object "protected" from inappropriate manipulation from the outside. (In fact, I will break the principle of this second aspect in my examples below. I just might return to deal with it in a supplementary piece.)

 

I will deal only with the first aspect here.

 

Consider the C example from above. We are calling a function circle_compute_area, and we are passing it a struct representing a circle. In some sense we are doubling up on info here.

 

And why do we keep the procedural stuff separate from the data stuff? What if we could glue them together so that, say, a circle, was something that held both it's "state" (the radius value, the area value) and its procedural "knowledge". I.e. what if we could glue together those two?

 

Well, you've guessed it already.. The thing in C++ that sort of corresponds to a struct type in C is a class.

 

I am now myself calling on even worse C++ purists to correct this. I plead to you - don't go into the discussion of structs and classes being almost equivalent. It will fog the scene at this m,oment. Later maybe, but not now, please!.

 

I will now show the C++ main function first, so that you can see how we get a cleaner syntax, and then well move on to the class definitions involved. All you need to know now is that I will b(eventually) have two classes (that is types as opposed to instances) called Circle and Square. (In C++ the convention is to use capitalized words for types.)

int main(int argc, char * argv[]) {
   Circle my_circle;
   my_circle.radius = 7;
   my_circle.compute_area();
   my_circle.print_area();

   // And again something similar for a square...
}

Now, that wasn't so hard, was it? Notice how we're now not both mentioning a function with an explicit type as part of its name, and passing it a struct of that type. Instead, were "talking to the object" (in OO talk "object" is an instance), whether its about it getting a value or it being "asked to do something".

 

Encapsulation is about keeping state and procedure together in one place! It makes sense since it makes for better organized code. It makes sense since it caters for a nicer syntax, removing "redundant" source code text.

 

So what does these classes look like? Well...

class Circle {
   int radius;
   double area;

   void compute_area() {
      area = radius * 2 * 3.1415;
   }

   void print_area() {
      printf("Area is %f", area);
   }
}

class Square {
   int side;
   double area;

   void compute_area() {
      area = side * side;
   }

   void print_area() {
      printf("Area is %f", area);
   }
}

Whoaaaa! That wasn't hard, was it?!

 

With your initial exaggeration that C++ is really simple out of the way, let's do some observations.

 

1) To the purists: Yes I've left out access qualifiers. Don't mention them! At least not yet..

 

2) Functions that are "inside" a class knows of the variables "inside" the class without any qualification. The variables have a scope over the complete class and functions can refer to them without any qualification.

 

3) Notice that I can have functions named the same in different classes? We've got rid of that pesky redundant textual information in our source code.

 

4) To the purists: No, I will not discuss the this-pointer here and now. I beg of you to refrain too.

 

Just to make sure you got it: I can create another object of type Circoe (or Square, if I wanted) and manipulate that object too.

 

int main(int argc, char * argv[]) {
   Circle my_circle;
   my_circle.radius = 7;
   my_circle.compute_area();
   my_circle.print_area();

   Circle my_other_circle;
   my_other_circle.radius = 42;
   my_other_circle.compute_area();
   my_other_circle.print_area();
}

I could fill 10 pages more just about this firts "pillar" called Encapsulation but I'll stop here. I need a brake. You take one too...

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

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

Thank you, dbrion!

 

That was a typo. As I said, I just type without testing...

 

If anyone finds an obvious typo, then please let me know and I'll correct it.

 

If alerts and corrections makes this thread messy I can eventually merge it all into one nice tutorial so don't you worry right now about making a mess. My "chapter posts" are easily to spot and locate by their headings, bold and underlined.

 

Now for that break... (Let's hope we get all the way to virtual tonight!)

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Mon. Jun 19, 2017 - 06:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

 

 

 

 

 

Well, I got so excited over finding an online C++ compiler, I had to write this part too..

 

The ridiculously short introduction to object-oriented programming in C++

Part 3 - C++ Inheritance

 

Tell me something.. What does Circles and Shapes have in common? Well, they both have an area, you say. And I go, yes, but you missed that they both know how to print their area, and have you noticed that the way they do that is identical?!

 

No, wouldn't it be nice if we didn't have to double up on that? (In real OO life, it  won't be about doubling. The factor will not be 2. It will be a substantially large integer..)

 

You guessed it. We can avoid that doubling up. We can declare a Class with the things that Circles and Squares have in common, and then let Circles and Squares inherit the things from that class. We will call that base class (or super class) Shape. By the way, notice how easy it is to give things really meaningful names?

 

Here we go..

 

class Shape {
   double area;

   void print_area() {
      printf("Area is %f\n", area);
   }
};

class Circle :  Shape {
   int radius;

   void compute_area() {
      area = radius * 2 * 3.1415;
   }
};

class Square : Shape {
   int side;

   void compute_area() {
      area = side * side;
   }
};

int main(int argc, char * argv[]) {
   Circle my_circle;
   my_circle.radius = 7;
   my_circle.compute_area();
   my_circle.print_area();

   Square my_square;
   my_square.side = 9;
   my_square.compute_area();
   my_square.print_area();

   Circle my_other_circle;
   my_other_circle.radius = 42;
   my_other_circle.compute_area();
   my_other_circle.print_area();

}

Observe the output from the above:

 

Area is 43.981000
Area is 81.000000
Area is 263.886000

 

There's only one thing I think needs to be mentioned regarding new syntax here: The notation

 

class Circle :  Shape
  ...

is to be read: Here is a definition of a class Circle which inherits from the class Shape.

 

Now for the important observations - of the code:

 

1. There is one implementation of print_area().

 

2. The variable area is defined in one place.

 

3. Both Circles and Squares obviously have an area and knows how to print it.

 

4. There are individual/specialized  implementations of the value needed for computing an area (Circle has radius, Square has side).

 

5. There are individual/specialized  implementations of the computations, but they both yield the value to the generalized area variable that both types have.

 

6. The name of the computation functions happens to be the same (it made sense that we shouldn't have specialized names of those functions). But, and that's a big BUT, they need not have the same name. We could have held on to our awkward habits of the deplorable C language ;-) and had

 

my_circle.compute_circle_area();
.
.
my_square.compute_square_area();
.
.

but that would just have been silly, IMO. I hope you agree, since that would mean that you've grasped "it".

 

The teaser is: When we get to the virtual thing that started all this the use of identical names becomes crucial... 

 

Now, I'm really gonna have that break! You too!

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Mon. Jun 19, 2017 - 07:04 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can you clarify the distinction between a "class" and an "object"? For this discussion, are they they same?

 

And, without derailing this beautiful presentation, what is this about: 

 main(int argc, char * argv[]) 

 

Great job. Can't wait for More From The Master!

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Jun 19, 2017 - 07:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:
Well, I got so excited over finding an online C++ compiler, I had to write this part too..

For us that are following along, using Studio7, you might want to "publicize" appropriately...

/*
 * GccApplication3.cpp
 *
 * Created: 6/19/2017 12:49:49 PM
 * Author : ltheusch
 */ 

#include <avr/io.h>
#include <stdio.h>


class Shape {
double area;

void print_area() {
		printf("Area is %f\n", area);
	}
};

class Circle :  Shape {
	int radius;

void compute_area() {
		area = radius * 2 * 3.1415;
	}
};

class Square : Shape {
	int side;

	void compute_area() {
		area = side * side;
	}
};

int main(void) {
	Circle my_circle;
	my_circle.radius = 7;
	my_circle.compute_area();
	my_circle.print_area();

	Square my_square;
	my_square.side = 9;
	my_square.compute_area();
	my_square.print_area();

	Circle my_other_circle;
	my_other_circle.radius = 42;
	my_other_circle.compute_area();
	my_other_circle.print_area();

    while (1) 
    {
    }
}

 

 

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

An object is just one instance of some class.

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

Thanks

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

, what is this about: 

 

 main(int argc, char * argv[]) 

Well , on PC(cygwin; gnuLinux) and alikes, you can give info to the executable (ex : gcc/g++ needs to know what to compile "gcc tt.c -o tt.out", pick up some options (a lot). argc is the number of space separated trings which ar given +1, argv is each of the argumements -argv[0] being the name of the executable; printf("%s succeeded\n" , argv[0]); is a valid -at least I hope, depending on success definition- instruction line on a PC /RPi. Of course, it works with gcc and g++ , and similar mechanisms exist for python -optarg, IIRC...- One can of course use the syntax : main() and the full syntax is int main(argc, char * argv[]){} -allows to test the exit status ....

Last Edited: Mon. Jun 19, 2017 - 07:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's not the full prototype for main() ;-)

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

Which raises the question: all other functions require prototypes. main() seems to be a sort of built-in or integral part of C/C++.  Just what is it's prototype?  

 

Do ISRs get by without prototypes because they are normally defined (in my code, anyway) before main()? It looks like (see msg #27) that C++ uses prototypes for ISRs. Is that because they are not defined before main()? Is this also true for plain C?

 

Jim

 

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

duplicate deleted

 

 

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Jun 19, 2017 - 08:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The full prototype allowed by gcc is:
.
int main(int argc, char * argv[], char *envp[])
.
Only makes sense on a hosted OS of course.

Last Edited: Mon. Jun 19, 2017 - 08:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Where do these arguments come from (in the embedded environment)?

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

My edit crossed your post. You need an OS for main() to receive anything but void.

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

Best break I've had all day! One fag and half a litre of water. (I do not allow myself the habit of smoking while at work, and we have near tropical heat here at the moment..)

 

Take a deep breath, because here it comes!

 

The ridiculously short introduction to object-oriented programming in C++

Part 4 - C++ Polymorphism, or virtual functions

 

So we dealt with what different shapes have in common.

 

Or did we? Think!

 

.

.

.

 

Yes, we missed one thing. Both Circles and Squares can compute their areas. "But they're doing it in a different way!", you shout. I calmly respond: "Yes, but the fact that they can do it is common." If you get the distinction you're really beginning to grasp the power of object-oriented programming!

 

We must make a small deviation here: pointers.

 

Yes, pointers. They're not different from pointers in C. They point to something. Let's just redo our example from the Inheritance chapter, but using pointers:

 

class Shape {
   double area;

   void print_area() {
      printf("Area is %f\n", area);
   }
};

class Circle : Shape {
   int radius;

   void compute_area() {
      area = radius * 2 * 3.1415;
   }
};

class Square : Shape {
   int side;

   void compute_area() {
      area = side * side;
   }
};

int main(int argc, char * argv[]) {
   Circle * my_circle = new Circle;
   my_circle->radius = 7;
   my_circle->compute_area();
   my_circle->print_area();

   Square * my_square = new Square;
   my_square->side = 9;
   my_square->compute_area();
   my_square->print_area();
}

So, what have changed?

 

1. My variables my_circle and my_square are now pointers to objects rather than being objects themselves.

 

2. We refer to members by dereferencing with the -> operator rather than the dot-operator (.), just as in C.

 

3. We can refer to both member variables and member functions that way.

 

4. Nothing has changed in the class declarations themselves.

 

With that done we can deal with the fact that Circles and Squares share the ability to compute their areas. Let's start with complementing the super-class with a compute_area() function. What should it produce as the area value? A shape in general can not compute its area. Let's, for the sake of the example, make a slightly dirty design (don't ask "Why dirty?", just accept ;-) ) and have -1 denote a non-computable area. Our Shape class would go

 

class Shape {
   double area;

   void compute_area() {
       area = -1;
   }

   void print_area() {
      printf("Area is %f\n", area);
   }
};

Let's experiment:

 

class Shape {
   double area;

   void compute_area() {
       area = -1;
   }

   void print_area() {
      printf("Area is %f\n", area);
   }
};

class Circle : Shape {
   int radius;

   void compute_area() {
      area = radius * 2 * 3.1415;
   }
};

class Square : Shape {
   int side;

   void compute_area() {
      area = side * side;
   }
};

int main(int argc, char * argv[]) {
   Circle * my_circle = new Circle;
   my_circle->radius = 7;
   my_circle->compute_area();
   my_circle->print_area();

   Shape * my_shape = new Shape;
   my_shape->compute_area();
   my_shape->print_area();
}

So far, so good. Just as we expected! (Now, remember that I did that for the sake of the argument - it is not necessarily good design, and only a demonstration we need to eventually get to The Good Stuff[tm].

 

Now for the next diversion. And this is both good design and important: Whenever you use a type (e.g. a pointer) to refer to an object of a class, you can also use a pointer to a super-class. I.e. you can

   Shape * my_shape = new Circle;

Read that again, just to make sure you see  what I changed!

 

This has implications, some of which you might not expect. Here's a new main() (everything else as before):

 

int main(int argc, char * argv[]) {
   Shape * my_shape = new Circle;
   my_shape->radius = 7;
   my_shape->compute_area();
   my_shape->print_area();
}

The compiler protests immediately:

 

 In function 'int main(int, char**)':
36:14: error: 'class Shape' has no member named 'radius'

Hmmm... It seems that the compiler does not understand that my Shape actually is a circle. And so we can't set its radius.

 

Time for another deviation then.. We need a constructor. Well' we actually already had one or we could not have instantiated the objects as we've deone above. But that was a default constructor that the compiler provided, and it essentially did nothing (purists: Stay quiet!).

 

A constructor is a special function that is called when an object is instantiated. it can take parameters. It can not return any value. It always has the same name ass the class itself. Here's our classes, some of them with explicit constructor supplied by me, the coder:

 

#include <stdio.h>

class Shape {
   double area;

   void compute_area() {
       area = -1;
   }

   void print_area() {
      printf("Area is %f\n", area);
   }
};

class Circle :  Shape {
   int radius;

   Circle(int a_radius) {
       radius = a_radius;
   }

   void compute_area() {
      area = radius * 2 * 3.1415;
   }
};

class Square : Shape {
   int side;

   Square(int a_side) {
       side = a_side;
   }

   void compute_area() {
      area = side * side;
   }
};

int main(int argc, char * argv[]) {
   Shape * my_shape = new Circle(7);
   my_shape->compute_area();
   my_shape->print_area();

   Shape * my_other_shape = new Square(9);
   my_other_shape->compute_area();
   my_other_shape->print_area();
}

See how we pass the values of the radius and the square at the time of construction? Now the compiler does not emit any error, but what happens when we run this? Well...

Area is -1.000000
Area is -1.000000

Still not that good. Obviously the compiler does not understand that the objects actually are one Circle and one Square at the time of the call to compute_area(). The compiler determines which compute_area() implementation to actually call based on the type of the reference (the type of the pointer) to the object, i.e it calls the Shape version of compute_area(). Sometimes this is desired behavior, but in our case it's not.

 

And finally we have arrived! You tell the compiler to call a member function based on the type of the object itself by declaring a function virtual. It goes like this:

 

 

 

In fact, we can do better than that - we can demand that anyone that inherits from Shape (e.g. declaring a class Triangle) must implement a function to compute the area. The super-class puts a demand on something that all sub-classes must implement, but without saying anything about how the sub-classes specifically does this. What is established in the super-class is the interface to the function: It's name, its parameters and its return type (in out case it's simple - no parameters and a void return type).

 

It goes like this:

 

 

 

 

 

 

#include <stdio.h>

class Shape {
   double area;

   virtual void compute_area()
   {
       area = -1;
   }

   void print_area() {
      printf("Area is %f\n", area);
   }
};

class Circle :  Shape {
   int radius;

   Circle(int a_radius) {
       radius = a_radius;
   }

   void compute_area() {
      area = radius * 2 * 3.1415;
   }
};

class Square : Shape {
   int side;

   Square(int a_side) {
       side = a_side;
   }

   void compute_area() {
      area = side * side;
   }
};

int main(int argc, char * argv[]) {
   Shape * my_shape = new Circle(7);
   my_shape->compute_area();
   my_shape->print_area();

   Shape * my_other_shape = new Square(9);
   my_other_shape->compute_area();
   my_other_shape->print_area();
}

And the output is...

Area is 43.981000
Area is 81.000000

Stop. Take a deep breath. Exhale...

 

What just happened was important. We made some objects referring to them using a general (as in non-specific) super-class and then, without being explicit about what types they where we called functions that exposed very specific behavior. Just to drive the point home, take a look at this main():

 

int main(int argc, char * argv[]) {
   // An array of pointers to Shapes
   Shape * my_shapes[3];
   // Fill it with different shapes
   my_shapes[0] = new Circle(15);
   my_shapes[1] = new Square(8);
   my_shapes[2] = new Circle(3);

   for (int i = 0; i<3; i++) {
       my_shapes[i]->compute_area();
       my_shapes[i]->print_area();
   }
}

with output

Area is 94.245000
Area is 64.000000
Area is 18.849000

What you should note is that

 

1. The array is an array of pointers to Shapes. There is nothing about the array that says anything about whether the pointers will point to Circles or Squares or, as I did above, a mix.

 

2. There is nothing in the for-loop that says anything about whether it is looping through pointers to Circles or Squares. It loops over pointers to Shapes.

 

Still, the output is correct. The  code asks the objects themselves to do what they specifically can do but the code in main (except for the instantiation) does not know about the specifics!

 

If that does not stun you, then I don't know what will.

 

Just one more thing before we round up: It might have crossed your mind that it is stupid to have an implementation of compute_area() in the Shape super-class. I agree. Let's get rid of it. We can tell the compiler that all classes that inherit from Shape must implement a compute_area() by doing this in Shape:

class Shape {
   double area;

   virtual void compute_area() = 0;

   void print_area() {
      printf("Area is %f\n", area);
   }
};

Notice the difference from before!

 

Not only does this put a demand on sub-classes to implement a compute_area(). It also tells the compiler that Shape has no implementation. The net effect is that now Shapes as such can not be instantiated - the compiler will emit an error if you try. The only instantiatable classes in our example now are Circle and Square. That makes a lot of sense to me, and  I hope to you too!

 

Now there's only one question you have left: WHY? Why this elaborate mechanism? What is it good for?

 

Here are some sketched examples:

 

You write drivers for serial communication for different hardwaare interfaces. Things they have in common is e.g. functions that are named send_char(), send_string() etc. They might all have a built in ring-buffer. But they are very specific as to hoe they are initialized. A serial port takes baudrate-databits-parity-stopbits. Some other serial interface has a different initialization. Share the common code and data structure in a superclass. This example might not be a good one for polymorphism, but then again it might.

 

Another example would be a font system. Al string can be rendered into a bitmap by a font so that's something common. But some fonts are proportional, others are not.

 

And Cliff was a wise man when he picked the Shapes example. It is one of the most used introductory examples to object-orientation. I think you can imagine a graphical system that not only does computation of areas, but that might actually have functions where objects can paint themselves on a canvas. In fact, one of the first applications where object orientation proved itself was "paint programs". And regardless of what graphical icons'n'windows'n'stuff OS you use I can assure you that it is object orientation behind the scenes to handle it. It might not be C++, but it's OO.

 

Finale: I'm sure I've missed out on some things, and made a mess about others. I also fully understand that this last part of this installation got BIG, but there was no other way about it. You aked about  virtual - you got it..

 

Now (well, tomorrow...): Ask questions! Point to spelign errors and blatant erors!

 

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Mon. Jun 19, 2017 - 08:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

@Lee: No, I will not publish the examples as avr-gcc code, ready for e.g. Atmel Studio. I firmly believe that you should learn C++ (and C for that matter), on a PC or similar. (Use an online compiler, or get one of the free C++ IDEs). Also, I have cut out things from the examples that are necessary for them to even compiler. I hinted about that above. Sorry, but you can not simply pick my examples above and build and run. I chose to do so to keep this as short as possible. I did not, e.g., wqnt to explain public/protected/private declarations so I threw them out before pasting my examples here. This is text to read, not code to run yourself.

 

@Jim: The arguments to main() is for coding with a PC as target (or, Mac in your case). See my reply to Lee just above for the rationale.

 

AND MOST IMPORTANT: This was the minimal "pedagogic journey" I could come up with to illustrate what virtual is about. I strongly argue that it is much more important ass a concept than ass a syntactic element in C++. BUT anyone I might have lured into the belief that this was actually a "Learn yourself C++ in three hours" deserve my apologies. The ambition was never anything more than trying to start from known C, illustrate superficially what is needed to know in order to superficially understand what virtual is about.

 

The rough tough fact is: Learning C++ will take time. It's not only that C++ is a large programming language (e.g. as compared to C that is regarded "minimal" by most connosieurs). It about adopting to "thinking object oriented". When I was exposed to OO the first time I has codded for 10 or 15 years. It took me a long time before I had cleansed myself from my C/Pascal thinking.

 

I speculate that the journey from C to object-oriented C++ actually is harder than going from assembler to C.

 

But the good thing is: You can use a C++ compiler, and utilize C++ code that someone else has written, without being an OO master. You still want to know, superficially, what goes on in that other persons code, and if it says virtual then the above is a shot at an explanation. If it also can serve as the first gentle (yes... gentle) introduction to OO and C++ then that a bonus.

 

I need to sleep now.. ;-)

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

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

The problem I have with the common "shapes" or "cars" examples always given for C++ is that it tells you nothing about how inheritance, polymorphism, encapsulation might actually be of any use when you program a real world example like an AVR.

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

Heat wave here, also. Will digest your latest post. Lots to chew on there. Then back to your first one, and try to integrate them together.

 

Huge gratitude!

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Heat wave here too. Is this "The day the Earth caught fire"?
.
Currently 10pm and I'm outside in shorts walking one of my Bengals as the house is like a furnace. Light just about going now.

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

I can give you the code below. It is the last example, complete. You can test it out e.g. here: http://www.cpp.sh/  At that site the compilation hangs from time to time when you try to run an example. Make sure you copy what you have in the editor window, then do a refresh of the page, and paste back what you copied. Nevertheless it seem to me to be a much better vehicle to learn C++ than to use Atmel Studio.

 

Whats in it that you have not seen?

 

1. The occurences of public. Each class has to start with e.g.

class Shape {
public:
 ...
}

This is not good design, but as I said I was not out to explain public/protected/private and the implications of it. That would need another two or  three posts...

 

Also, when inheriting you need a public there too.

class Circle : public Shape {
public:
   ...
}

And you need to start with (not a surprise, I hope):

#include <stdio.h>

 

COMPLETE CODE FOR THE LAST EXAMPLE:

#include <stdio.h>

class Shape {
public:
   double area;

   virtual void compute_area() = 0;

   void print_area() {
      printf("Area is %f\n", area);
   }
};

class Circle :  public Shape {
public:
   int radius;

   Circle(int a_radius) {
       radius = a_radius;
   }

   void compute_area() {
      area = radius * 2 * 3.1415;
   }
};

class Square : public Shape {
public:
   int side;

   Square(int a_side) {
       side = a_side;
   }

   void compute_area() {
      area = side * side;
   }
};

int main(int argc, char * argv[]) {
   // An array of pointers to Shapes
   Shape * my_shapes[3];
   // Fill it with different shapes
   my_shapes[0] = new Circle(15);
   my_shapes[1] = new Square(8);
   my_shapes[2] = new Circle(3);

   for (int i = 0; i<3; i++) {
       my_shapes[i]->compute_area();
       my_shapes[i]->print_area();
   }
}

Good night! (-:

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Mon. Jun 19, 2017 - 09:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Crap, double post. Sorry..

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Mon. Jun 19, 2017 - 09:07 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Heatwave here is a mere 30C. But this is a temperate maritime area where this time of year, much above 25C is pretty rare.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

clawson wrote:
The problem I have with the common "shapes" or "cars" examples always given for C++ is that it tells you nothing about how inheritance, polymorphism, encapsulation might actually be of any use when you program a real world example like an AVR.

For polymorphism I might agree. But encapsulation and inheritance are usable even in the smallest of applications IMO. And unless the compiler is crap, those two come with NO extra cost. You know my rant re this so I will digress.. ;-)

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

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

JohanEkdahl wrote:
@Lee: No, I will not publish the examples as avr-gcc code, ready for e.g. Atmel Studio. I firmly believe that you should learn C++ (and C for that matter), on a PC or similar.

 

OK, fair enough.  But let's say I've done some C++ and a fair amount of C#.  Now, given the thread is in the main MegaAVR and TinyAVR forum, you and Cliff appear to be proselytizing for the use on such platforms.  As such, I want to explore 

clawson wrote:

So those who would say "ah but C++ is large and bloaty compared to C" have once again been proven to have unfounded concerns. ;-)

...and your implied "defense":

JohanEkdahl wrote:

You just wait, Cliff. Soeone will come along with a splendidly ignorant argument..

 

Giving use of a generic/PC-based primer here -- what is the use if the examples cannot be built?  Indirectly, my "Hello World!" snipe enforces that.

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

To a significant degree Lee has a good point. Being able to build example code is critical for learning. And, for the best learning, one should have to work through some of those details, on ones own (small coin of the realm). At least at the start, that was what I was asking about. How do you deal with include files (and what is "needed", like avr/io.h). I think that was answered. Likewise, how to call C functions from C++ code. That was pretty well answered. (Thanks for both!) Johan's code in #58 does a lot more (thanks).  But, as critical as the concepts are, it is also critical to be able to write code that embodies the idea. And, have it run! That online tool is nice, but it seems like a parallel universe compared to Atmel Studio 7.

 

Johan, with that latest post of yours, I will have hours (and more hours) to digest and try. Thank you for your hard work and the supporting contributions that everyone else has put in to the sausage grinder. At that point, you don't even care whether or not the pig has lipstick!

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Jun 19, 2017 - 10:13 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Johan -

 

You are a Prince! Maybe even a King!

Now let's be careful about that......or it's off with your headers....

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
My big question, though: how does one "instantiate" a class? I see code like:

RH_RF95 rf95;

Is that the instantiate statement?

Yes,  it says that there is a class RH_RF95 that is defined [most likely] in the file RH_RF95.h  An object named: rf95  of the class: RH_RF95 is being created (instantiated) in SRAM.

 

The source code for the class will be in the file: RH_RF95.cpp.  Arduino will look for this file in one of the three semi-secret folders where Arduino will only look for library source. 

The easiest to find is in the subfolder "libraries" in the  <Arduino_Sketches> folder.

 

The SRAM object is a collection of variables and function pointers.  The binary for the functions in class libraries that are called by the .ino file are stored in flash. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
C:\SysGCC\avr\bin>avr-nm lcd.elf | grep " T"
:
000001ba T _Z10lcd_clrscrv
000001a4 T _Z10lcd_gotoxyhh
00000206 T _Z10lcd_puts_pPKc
00000180 T _Z11lcd_commandh
000000d6 T _Z22wait_until_key_pressedv
00000192 T _Z8lcd_datah
000001c0 T _Z8lcd_homev
00000224 T _Z8lcd_inith

 

By the way, most of the gnu tools (nm, objdump) have a "-C" switch that will de-mangle names for you:

 

avr-nm -SC *.elf | more    

000001ea 00000042 t HardwareSerial::_tx_udr_empty_irq()
000001a2 0000001c t HardwareSerial::peek()
000001be 00000018 t HardwareSerial::available()
0000010e 00000016 t Print::print(char) [clone .constprop.10]
00000124 00000056 t Print::write(unsigned char const*, unsigned int)
0080023c 00000001 b SPIClass::initialized

 

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

int main(int argc, char * argv[], char *envp[])

well, envp is compiler-specific according to https://stackoverflow.com/questi...

and it is seldom used (I prefer getenv)

Only makes sense on a hosted OS of course.

Maybe an command line interpreter would be enough (keeping an array of chars ... and a list of {name, char[]}) if dynamic links are/were supported  -needs at least kind of disk... appleIIe had, without having an  full OS IIRC...-

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

Examples can be built on a PC... (cygwin/ virtual box) or a xxPi...

Edited : with minor modifications -3 lines- , or -the last one- as it is...

 

BTW : for everything that is not avr -or other MCU- specific, using a PC, already soldered (avoids trouble shouting issues, which have nothing to do with learning -at least they should) makes things easier (and, anyway, one needs a PC /xxPi to cross compile & program). This avoids pluging a serial line (baudrate is never the right one), a LCD (sometimes, 3 lines and 25 chars would be very pleasant to verify math results are not absurd)

Last Edited: Tue. Jun 20, 2017 - 08:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

js wrote:

Johan -

 

You are a Prince! Maybe even a King!

Now let's be careful about that......or it's off with your headers....


Well, at least I'll go out with Class..
Seriously, in the conext given, the real kings where Kristen Nygaard and Ole-Johan Dahl. Google the names for more. Turns out that Object-Oriented Programming was a reality even long before K&R thought up the C programming language...

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Tue. Jun 20, 2017 - 07:39 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If one is a purist, the area of a circle il pi*radius*radius (is between the area of a sqare with size 2*r == 4*r*r (higher bound) , and the area of a smaller sqare, with size sqrt(2) r -> lower bound  area is 2*r*r.

 

 


private :
   void compute_area() {
      area = radius * radius * 3.1415;// pi is dimensionless, radius is meters -> area in m**2
   }
};

One can add rectangles, too,  initialized two ways :

both sizes are given;

only one size is given and one assumes it is a square (but it is not)

 

using an array of shapes, and computing (with old eyes, slow optical nerve and maybe a too warm brain) its number of elements is tedious and error prone (one never knowhs why the last element is not printed, if one adds an item) life seems easier on the long term with:

 

 Shape * mShapes[] = {new Circle(15),  new Square(8), new Rectangle(7,11),
      new Rectangle(22)};
    for (int i = 0; i<sizeof( mShapes) /sizeof mShapes[1];i++) {
       mShapes[i]->compute_area();
       mShapes[i]->print_area();
    }

Edited : tiny porion of typos removed; add dimension argument to know whether a formula is likely

Last Edited: Tue. Jun 20, 2017 - 11:02 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Johan,

   And to think I just purchased a C++ book from Amazon and could have saved a few $$$.

Maybe it will still be useful as reference........ 

 

Jim

 

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

Let me assure you that if that book is not crap I've covered just a few percent of it.

I repeat: C++ is a huge language.

What book, by the way?

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Tue. Jun 20, 2017 - 08:41 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Let me assure you that if that book is not crap I've covered just a few percent of it.

I repeat: C++ is a huge language.

 The usual problem with C++ books WRT embedded systems is that the books spend a lot of time talking about standard C++ libraries like C++ STL or Boost that are invaluable for solving Computer Science Problems on a PC, but generally need to be avoided on an embedded system (because they're heavily dependent on having lots of dynamically allocateable RAM.)

 

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

Scott meyers has some stuff on embedded c++ that is worth a read. I think he charges $20 or $30 bucks. He details the inner goings on like virtual method tables and static vs dynamic allocation so you can unserstand the 'cost' of using certain c++ features from an embedded perspective. I found it good value.

If you use the arduino core code as a guide, this should assist in using the applicable features of C++ for the pragmatic embedded programmer.

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

I seem to recall that C++ does some bounds checking on array access. Does this still happen in the embedded world? Assuming that it does, does it slow down programs that are heavy array users (like transmit and receive buffers or uSD read/write buffers)? Does this happen on C code that is called from a C++ class?

 

What does it do on an attempted buffer over-run? Are there exceptions? If so, what does it do when one occurs? Do we need to write exception handlers?

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Wed. Jun 21, 2017 - 01:36 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It only does bounds checking if you write code to do it. If you write a smart array object, then you can implement such features. Otherwise is just like C- no safety net.
Note: exceptions can be costly on an embedded system.

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

Thanks Kartman -

 

I could not imagine exception handling that was not costly, that is why I asked (especially pertinent if there is automatic bounds checking).

 

Thanks for the clarification.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Forgot to mention : Virtual functions rely on tables of function pointers, known as v-tables. Although the function pointers are known at build time avr-gcc places them in SRAM rsther than flash memory. This is a deficiency of avr-gcc, not of C++ as a language. Approximate cost is:
One v-table per class (not object) that has virtual functions. Size of table is number of virtual functions times size of function pointer (16 bits?).

 

EDIT: Addendum: Each object also bears with it a cost if using virtual functions - a pointer to the applicable v-table. But this is so in any implementation and is not a deficiency. (And you can now infer what happens when a virtual function is used: De-reference the objects v-table pointer to get to the table. Then de-reference the applicable function pointer in the v-table, doing a call to the function itself. I.e. a double indirection.  Actually, the "as-if" rule applies here. An implementation can chose another implementation as long as it is functionally equivalent to this - but I have yet to see a compiler that does not actually do it this way..)

"He used to carry his guitar in a gunny sack, or sit beneath the tree by the railroad track. Oh the engineers would see him sitting in the shade, Strumming with the rhythm that the drivers made. People passing by, they would stop and say, "Oh, my, what that little country boy could play!" [Chuck Berry]

 

"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]

Last Edited: Thu. Jun 22, 2017 - 03:30 AM