Encapsulation & Accessibility in C++

Encapsulation : 

Encapsulation is another major component of OOP . the process of restricting access to data is called encapsulation. Encapsulation can be used to hide data members and members' functions. 

note: Class members are actually Class variables(data) and functions 

Access modifiers: 

Access modifiers are keywords in OOP that set the accessibility of classes, methods, and other members. Access modifiers are a specific part of programming language syntax used to facilitate encapsulation. in C++ there are 3 access modifiers 
  1. public 
  2. protected
  3. private 

Explanation:

Suppose you have created a class A. 
Then The private, public and protected members of class A can be accessed within the class. Now say you have created an object of this class then the object can only access the public members of the class. 

Similarly, if you derive a class B from the base class A then the derived class B can access the protected & public members of the base class A

now if you create an object of type Class B then the object can only access the public members of class B. now whether this object can access the members of the Base class A depends on the inheritance type. if it's public then this object of type class B can access public members of class A . on the other hand if the inheritance type is private then this object of type class B won't be able to access public members of class A.   

Now see the following diagram and you will understand everything 

Access specifiers without inheritance: 

Access specifiers with an inheritance: 










C++ in Arduino : Inheritance

What is Inheritance?

Inheritance is the process of creating new classes called "derived classes" from existing classes called "base classes". the derived class inherits all the capabilities of the base class & can add refinements of its own.

But why implement inheritance?

The answer is inheritance permits code re-usability. Suppose that you have made or got a class that is working perfectly but you want to add your own features to that class . Now instead of modifying the codes of Class A you can create a new class and inherit all the properties of Class A . in this way if any bug occurs you will be sure that it is from your newly made class thereby simplifying development by a lot. Inheritance is a major element of OOP.

the derived class is also called the child class
the base class is also called the Parent class

In some languages, the base class is called the superclass and the derived class is called the
subclass.

you will also hear the term child class extending parent class which is actually the same thing i.e. child class inheriting properties of the base/parent class.

let's see inheritance in UML with a very simple example

         
Here Animal is the Base class And Dog is the Derived Class . as Dog is an animal too so it will have all the capabilities of an animal. 

In UML inheritance is called generalization. In UML class diagrams, generalization is indicated by a triangular arrowhead on the line connecting the Derived and Base classes. The Arrow means derived from. The direction of the arrow tells that the derived class refers to functions and data in the base class, while the base class has no access to the derived class. 

Code in Action 

Lots of theory now Let's jump into Arduino and write some practical codes.

Returning to the Sensor Class that we made in our previous post  https://whileinthisloop.blogspot.com/2016/05/c-in-arduino-isr-in-class.html We have a proper working Sensor Class . and let's say we want to use this class For temperature measurement and we want to add functions like 
  • readCelsius() 
  • readFahrenheit() 
Now we can add these extension of ours using inheritance and by doing so we won't have to make any modification to the Sensor class, So to do that let's make a New Class called LM35 which will extend Sensor Class . in UML : 

here are the code snippets for the Sensor Class : 

Sensor.h 

#ifndef _SENSOR_H      //tells compiler to compile this class only once 
#define _SENSOR_H 

#include <Arduino.h>

class Sensor
{
   public:
          Sensor();
          void init(void);
          uint16_t readRawAdcValue(void);
          float readmv(void);
};
#endif

Sensor.cpp

#include "Sensor.h"

volatile static uint16_t adcValue = 0;

Sensor::Sensor()                                             
{}

void Sensor::init(void)
{
  ADMUX|=(1<<REFS0);    //AVCC with external capacitor at AREF pin 
  ADMUX|=(1<<MUX2)|(1<<MUX0); //0101adc channel 5 selected for adc input
  ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);   //prescalar 128 selected 
  ADCSRA|=(1<<ADEN);    //adc enabled
  ADCSRA|=(1<<ADATE);   // ADC Auto Trigger Enable
  ADCSRA|=(1<<ADIE);   //adc interrupt enable such that interrupt will happen after each conversion
  ADCSRA|=(1<<ADSC);   //conversion process begins
  sei();
}

uint16_t Sensor::readRawAdcValue(void)
{
  return adcValue;
}

float Sensor::readmv(void)
{
  return((readRawAdcValue()/1024.0)*5000);
}

ISR(ADC_vect)       //adc interrupt service routine /tasks which will be executed by microprocessor after each conversion
{
adcValue=ADCL;        //ADCL data register read 
adcValue+=(ADCH<<8);  //ADCH data register read
}

LM35.h

#ifndef _LM35_H      //tells compiler to compile this class only once 
#define _LM35_H 

#include <Arduino.h>
#include "Sensor.h"

class LM35:public Sensor     //LM35 class inherits base class Sensor
{
   public:
          LM35();
          float readCelsius(void);
          float readFahrenheit(void);
};
#endif

Notice that after colon : the base class name is written. this sets up the relationship between the parent and child class.

The keyword public after the colon 
: specifies that objects of the derived class LM35 are able to access public member functions of the base class Sensor. The alternative is the keyword private. When this keyword is used, objects of the derived class LM35 cannot access public member functions of the base class Sensor.

LM35.cpp

#include "LM35.h"

LM35::LM35()                                             
{}

float LM35::readCelsius(void)
{
  float mv = readmv();
  float cls = mv/10.0;
  return cls;
}

float LM35::readFahrenheit(void)
{
  float cls = readCelsius();
  float fhr = (cls*9)/5 + 32;
  return fhr;
}

Now let's write the sketch which will create an object of LM35. 

the sketch 

/*
 * this example shows the inheritance of c++ 
 * author: Hassin Ayaz
 * http://whileinthisloop.blogspot.com/
*/
#include "LM35.h"

LM35 myTempSensor;

void setup() 
{
  Serial.begin(9600);
  myTempSensor.init();
}

void loop() 
{
  Serial.print("raw ADC value: ");
  Serial.println(myTempSensor.readRawAdcValue());

  Serial.print("in mv: ");
  Serial.println(myTempSensor.readmv());

  Serial.print("in celsius: ");
  Serial.println(myTempSensor.readCelsius());

  Serial.print("in fahrenheit: ");
  Serial.println(myTempSensor.readFahrenheit());
  Serial.println("-----------------------------");
  delay(1000);
}

here you can see that the LM35 type object myTempSensor can call functions of both the base & derived classes.

In this post we have seen how inheritance of OOP can increase the usability of our codes :)

All the sources are available in this link https://github.com/hassin23ayz/ArduinoCpp/tree/master/T37_inheritance

C++ in arduino : ISR() in a class

In the last post https://whileinthisloop.blogspot.com/2016/04/c-in-arduino-interrupted-adc-with-free.html I showed you how to run the ADC in free-running mode. In this post I am going to show you how to wrap up the ISR() in a Class

I don't know if you have tried it or not but implementing ISR() in a class is a bit tricky ;)

Handling interrupts is not only a job for C or assembly language actually  C++ can also handle it. In a later post, I will show how to wrap up the ISR() with the help of some advanced concepts of c++ like virtual function. 

But in this post, I am going to show you a quick & simple way that may help you if you don't understand advanced concepts of C++. So if you are willing to design and maintain OOP concepts and want to implement your  ISR() in a class in a simple way then this post may help you.

Note: The way I am going to show is kind of mixing C & C++ concepts.

To keep our embedded context relevant, we are going to make a Sensor Class from the last post's codes,

First Let us make the declaration file (sensor.h)


#ifndef SENSOR_H_     //tells compiler to compile this class only once 
#define SENSOR_H_

#include <Arduino.h>
#include <avr/interrupt.h>

class Sensor
{
    //variables
  public:
  private:

    //functions
  public:
    Sensor();
    ~Sensor();
    void init(void);
    unsigned int readAdcValue(void);
};

#endif

you can see that there is no ISR(ADC_vect) function declaration in the header file.

now let's make the implementation file (sensor.cpp)

#include "Sensor.h"

static unsigned int adcValue; //this variable's scope is only this file 

//constructor
Sensor::Sensor()
{}

//destructor
Sensor::~Sensor()
{}
void Sensor::init(void)
{
  //initiating variables
  adcValue = 0;

  //initiating registers
  ADMUX |= (1 << REFS0); //AVCC with external capacitor at AREF pin
  ADMUX |= (1 << MUX2) | (1 << MUX0); //0101adc channel 5 selected for adc input
  ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); //prescalar 128 selected ADC clk = 125KHz
  ADCSRA |= (1 << ADEN); //adc enabled
  ADCSRA |= (1 << ADATE); // ADC Auto Trigger Enable
  ADCSRA |= (1 << ADIE); //adc interrupt enable such that interrupt will happen after each conversion
  ADCSRA |= (1 << ADSC); //conversion process begins
  sei();                 //global interrupt enabled
}

unsigned int Sensor::readAdcValue(void)
{
  return adcValue;
}


ISR(ADC_vect)       //adc interrupt service routine /tasks which will be executed by microprocessor after each conversion
{
  adcValue = ADCL;      //ADCL data register read
  adcValue += (ADCH << 8); //ADCH data register read
}

Note that the adcValue variable is a  static unsigned int type global variable & this variable gets updated in the interrupt service routine but why I have not declared this variable as a Class member variable? the answer is as 
the ISR() function is not a Class member function you won't be able to access any Class member variable in the ISR() .  

Also, note that the adcValue is a static variable which means its scope is limited to the cpp implementation file only. This pattern provides a data abstraction like private variables in a class. This is kind of implementing a C++ feature in C.

As per language pattern One may argue on the above approach but I found this simple solution working without any runtime / compile time issue but obvisously there may be better ways to implement.

Categories

Pages

Firmware Engineer

My photo
Works on Firmware, Embedded Linux, Smart Metering, RTOS, IoT backend

Contact Form

Name

Email *

Message *

Copyrighted by Hassin. Powered by Blogger.