How to Use Lambda Expressions and File I/O in C++

In this tutorial, we will explore lambda expressions and file I/O using C++.

Prerequisites

Using Lambda Expressions 

Let’s explore lambda expressions in C++ and how they can be used in robotics projects. 

Lambda expressions allow us to define anonymous functions inline, which is particularly useful for operations like sorting sensor data or defining quick callback functions. 

Open a terminal window, and type this:

cd ~/Documents/cpp_tutorial
code .

Create a new C++ file and name it lambda_sensors.cpp

Type the following code into the editor:

#include <iostream>
#include <vector>
#include <algorithm>  // Include for std::sort

int main() {
    // Example list of sensor readings (could be temperatures, distances, etc.)
    std::vector<int> sensorReadings = {90, 85, 60, 75, 100};

    // Print original readings
    std::cout << "Original readings: ";
    for (int reading : sensorReadings) {
        std::cout << reading << " ";
    }
    std::cout << std::endl;

    // Using a lambda expression to sort the readings in descending order
    std::sort(sensorReadings.begin(), sensorReadings.end(), [](int a, int b) {
        return a > b;  // Comparison criterion defined inline
    });

    // Print sorted readings
    std::cout << "Sorted readings (desc): ";
    for (int reading : sensorReadings) {
        std::cout << reading << " ";
    }
    std::cout << std::endl;

    return 0;
}

In this code, we start with a vector of integers representing sensor readings. The lambda expression is used within the std::sort function. Here, the lambda (int a, int b) { return a > b; } defines an inline function that tells std::sort how to compare two elements, specifying that we want to sort in descending order without the need for a separate function.

Run the code.

1-lambda-sensors

You should see the original and sorted readings displayed, showcasing how lambda expressions facilitate custom sorting in a concise manner.

This example highlights the utility of lambda expressions in C++, allowing for immediate definition of small scope functions directly within the code where they are needed. 

Iterating with for_each Loop

Let’s explore how to use the for_each loop in C++. The for_each loop is part of the C++ Standard Library and provides a clear, concise way to perform operations on every element in a range, such as an array or a container like a vector. This is particularly useful in robotics for tasks like processing sensor data, where you need to apply the same operation to a series of values.

Let’s demonstrate the use of the for_each loop with an example where we’ll process a list of distances recorded by a robot’s sensors.

Create a new C++ file and name it for_each_loop.cpp.

Type the following code into the editor:

#include <iostream>
#include <vector>
#include <algorithm>  // Include for std::for_each

int main() {
    // Example list of distances measured by sensors (in meters)
    std::vector<int> distances = {5, 10, 3, 7, 9};

    // Print original distances
    std::cout << "Original distances: ";
    for (int distance : distances) {
        std::cout << distance << " ";
    }
    std::cout << std::endl;

    // Using a lambda expression with std::for_each to increment each distance by 1
    std::for_each(distances.begin(), distances.end(), [](int &d) {
        d += 1;  // Increment each element by 1
    });

    // Print updated distances
    std::cout << "Updated distances: ";
    for (int distance : distances) {
        std::cout << distance << " ";
    }
    std::cout << std::endl;

    return 0;
}

In this example, we begin with a vector of integers representing distances recorded by a robot. We use std::for_each along with a lambda expression that increments each element in the vector. The lambda (int &d) { d += 1; } directly modifies each element by adding 1, demonstrating an efficient way to process and update data in-place.

Run the code.

2-for-each-loop

You should see both the original and updated distances displayed, showing the practical application of the for_each loop in modifying elements directly.

Handling File Input and output

Let’s explore how to handle file input and output in C++ and its applications in robotics projects.

File input and output are essential operations in many programs, including robotics applications. In C++, the <fstream> library provides classes for reading from and writing to files. The ifstream class is used for reading from files, while the ofstream class is used for writing to files.

Let’s create a new C++ file named file_io_example.cpp to demonstrate file input and output.

Type the following code into the editor:

#include <iostream>
#include <fstream>
#include <string>

int main() {
    std::string filename = "robot_data.txt";
    std::ofstream output_file(filename);

    if (output_file.is_open()) {
        output_file << "Sensor1: 10.5\n";
        output_file << "Sensor2: 20.7\n";
        output_file << "Sensor3: 15.2\n";
        output_file.close();
        std::cout << "Data written to file: " << filename << std::endl;
    } else {
        std::cout << "Unable to open file for writing." << std::endl;
    }

    std::ifstream input_file(filename);
    std::string line;

    if (input_file.is_open()) {
        while (std::getline(input_file, line)) {
            std::cout << line << std::endl;
        }
        input_file.close();
    } else {
        std::cout << "Unable to open file for reading." << std::endl;
    }

    return 0;
}

In this code, we first create a file named “robot_data.txt” using an ofstream object. We check if the file is successfully opened using the is_open() function. If the file is open, we write some sensor data to the file using the << operator and then close the file.

Next, we read the data from the same file using an ifstream object. We check if the file is successfully opened for reading. If the file is open, we read the file line by line using getline() and print each line to the console. After reading, we close the file.

Run the code.

3-file-io-example

The output confirms that the data was written to the file and then displays the contents of the file.

In robotics projects, file I/O can be used for various purposes, such as saving sensor data, reading configuration files, or storing robot states for analysis or debugging.

Thanks, and I’ll see you in the next tutorial.

Keep building!

How to Use Structs and Operator Overloading in C++

In this tutorial, we will explore how to use structs and operator overloading in C++. 

Prerequisites

Working with Structs

Let’s explore how to work with structs in C++ and their application in robotics projects. 

Structs can be used in C++ to group related data together, making the code more organized and readable. Structs are particularly useful in robotics projects for representing various data structures, such as robot configurations, sensor readings, or control parameters.

Open a terminal window, and type this:

cd ~/Documents/cpp_tutorial
code .

Create a new C++ file named robot_struct.cpp.

Type the following code into the editor:

#include <iostream>
#include <string>

struct RobotSpec {
    std::string model;
    int max_speed;
    double weight;
};

void print_robot_spec(const RobotSpec& spec) {
    std::cout << "Model: " << spec.model << std::endl;
    std::cout << "Max Speed: " << spec.max_speed << " m/s" << std::endl;
    std::cout << "Weight: " << spec.weight << " kg" << std::endl;
}

int main() {
    RobotSpec robot = {"MobileBot", 5, 10.5};
    print_robot_spec(robot);
    return 0;
}

In this code, we define a struct called RobotSpec to represent the specifications of a robot. It contains three members: model (a string), max_speed (an integer), and weight (a double).

We also define a function print_robot_spec() that takes a constant reference to a RobotSpec object and prints its members.

In the main() function, we create an instance of the RobotSpec struct called robot and initialize its members. 

We then pass robot to the print_robot_spec() function to print its specifications.

Run the code.

1-working-with-structs

The output displays the robot’s specifications as defined in the RobotSpec struct.

Implementing Operator Overloading

Let’s explore the concept of operator overloading in C++ and its applications in robotics projects.

Operator overloading is a powerful feature in C++ that allows you to define custom behavior for operators when applied to user-defined types. This means you can make operators like +, -, *, /, and others work with your own classes and objects in a way that makes sense for your specific use case.

Let’s create a new C++ file named distance.cpp to demonstrate operator overloading.

Type the following code into the editor:

#include <iostream>

class Distance {
public:
    int meters;

    Distance(int m = 0) : meters(m) {}

    Distance operator+(const Distance& other) const {
        return Distance(meters + other.meters);
    }
};

int main() {
    Distance d1(5);
    Distance d2(10);
    Distance sum = d1 + d2;
    std::cout << "d1 = " << d1.meters << " meters" << std::endl;
    std::cout << "d2 = " << d2.meters << " meters" << std::endl;
    std::cout << "sum = " << sum.meters << " meters" << std::endl;
    return 0;
}

In this code, we define a class called Distance to represent a distance in meters. We overload the + operator by defining the operator+() member function. This function takes another Distance object as a parameter and returns a new Distance object that is the sum of the two distances.

In the main() function, we create two Distance objects, d1 and d2, and compute their sum using the overloaded + operator. We then print the distances and their sum using the meters member variable.

Run the code.

2-operator-overloading

The output displays the distances d1 and d2, as well as their sum, in meters.

By overloading the + operator, we’ve made it possible to add two Distance objects together using the familiar syntax d1 + d2, which makes the code more intuitive and readable.

In robotics projects, operator overloading can be useful for performing operations on custom data types, such as distances, angles, or time durations, making the code more expressive and easier to understand.

Thanks, and I’ll see you in the next tutorial.

Keep building!

How to Use Inheritance and Abstraction in C++

In this tutorial, we will explore inheritance and abstraction in C++.

Prerequisites

Implementing Inheritance

Let’s explore one of the fundamental concepts of object-oriented programming in C++: inheritance. Inheritance enables us to derive new classes from existing ones, facilitating code reuse and simplifying the management of complex robotic software systems.

Open a terminal window, and type this:

cd ~/Documents/cpp_tutorial
code .

We’ll demonstrate how inheritance can be effectively used to model different types of sensors in robotics, such as temperature sensors and distance sensors, which share common features but also exhibit unique behaviors.

Let’s create a new C++ file and name it robot_sensors_inheritance.cpp.

Type the following code into the editor:

#include <iostream>

// Base class
class Sensor {
public:
    virtual void readData() = 0;  // Pure virtual function making Sensor an abstract class
};

// Derived class for temperature sensor
class TemperatureSensor : public Sensor {
public:
    void readData() override {
        std::cout << "Reading temperature data..." << std::endl;
    }
};

// Derived class for distance sensor
class DistanceSensor : public Sensor {
public:
    void readData() override {
        std::cout << "Reading distance data..." << std::endl;
    }
};

void testSensor(Sensor& sensor) {
    sensor.readData();  // Demonstrating polymorphic behavior
}

int main() {
    TemperatureSensor temp_sensor;
    DistanceSensor dist_sensor;

    testSensor(temp_sensor);
    testSensor(dist_sensor);

    return 0;
}

This code establishes a base class called Sensor with a virtual function readData(). 

We then create two derived classes, TemperatureSensor and DistanceSensor, each overriding the readData() method to perform their specific type of data reading. The testSensor function takes a reference to a Sensor, demonstrating polymorphism by allowing either sensor type to be tested interchangeably.

Run the code.

1-robot-sensors-inheritance

You should see “Reading temperature data…” followed by “Reading distance data…”, showing how our inheritance structure allows different sensor behaviors through a common interface.

Creating Objects

Let’s focus on one of the foundational skills in C++ programming—creating objects. Objects are instances of classes and are central to almost every C++ application, including robotics, where they represent and manage various hardware and software components.

We’ll walk through an example to illustrate the process of creating objects from a class.

Now, let’s create a new C++ file and name it creating_objects.cpp.

Type the following code into the editor:

#include <iostream>

// Define a class called Robot
class Robot {
public:
    // Constructor that prints a message when a Robot object is created
    Robot() {
        std::cout << "Robot object created." << std::endl;
    }
};

int main() {
    Robot my_robot;  // Creating an object of the Robot class
    return 0;
}

In this code, we define a class named Robot. The class includes a constructor, which is a special function that runs whenever a new object of the class is created. 

Here, the constructor prints a message to the console. 

In the main function, we create an object called my_robot from the Robot class, which triggers the constructor and its print statement.

Run the code.

2-creating-a-class

You should see the message “Robot object created.” indicating that our Robot object was successfully instantiated.

This example demonstrates the basic process of creating objects in C++. Understanding how to instantiate and use objects is essential for developing more complex robotic systems.

Defining Abstract Classes

Let’s explore the concept of abstract classes in C++ and how they can be applied in robotics projects. 

Abstract classes provide a blueprint for derived classes and enforce the implementation of certain methods, ensuring a consistent interface across related classes.

Let’s create a new C++ file and name it robot_abstract_class.cpp.

Type the following code into the editor:

#include <iostream>

class Robot {
public:
    virtual void move() = 0;
    virtual void sense() = 0;
};

class MobileRobot : public Robot {
public:
    void move() override {
        std::cout << "Mobile robot is moving." << std::endl;
    }

    void sense() override {
        std::cout << "Mobile robot is sensing its environment." << std::endl;
    }
};

int main() {
    MobileRobot mobile_robot;
    mobile_robot.move();
    mobile_robot.sense();
    return 0;
}

In this code, we define an abstract base class called Robot. It contains two pure virtual functions: move() and sense(). These functions have no implementation in the base class and must be overridden by derived classes.

We then create a derived class called MobileRobot that inherits from the Robot class. The MobileRobot class provides implementations for the move() and sense() functions, which are specific to mobile robots.

In the main() function, we create an instance of the MobileRobot class called mobile_robot. We then call the move() and sense() functions on this object.

Run the code.

3-robot-abstract-class

You should see the output indicating that the mobile robot is moving and sensing its environment.

This example demonstrates how abstract classes can be used to define a common interface for related classes in robotics. By enforcing the implementation of certain methods, abstract classes ensure that derived classes adhere to a specific contract, making the code more modular and maintainable.

Overriding Functions

Let’s explore the concept of function overriding in C++ and how it can be applied in robotics projects. 

Function overriding allows derived classes to provide their own implementation of functions defined in the base class, enabling polymorphic behavior.

Let’s create a new C++ file and name it robot_function_overriding.cpp.

Type the following code into the editor:

#include <iostream>

class Robot {
public:
    virtual void introduce() {
        std::cout << "I am a robot." << std::endl;
    }
};

class IndustrialRobot : public Robot {
public:
    void introduce() override {
        std::cout << "I am an industrial robot." << std::endl;
    }
};

int main() {
    Robot* robot = new Robot();
    IndustrialRobot* industrial_robot = new IndustrialRobot();

    robot->introduce();
    industrial_robot->introduce();

    delete robot;
    delete industrial_robot;

    return 0;
}

In this code, we define a base class called Robot with a virtual function introduce(). The introduce() function in the base class prints a generic introduction message.

We then create a derived class called IndustrialRobot that inherits from the Robot class. The IndustrialRobot class overrides the introduce() function to provide its own implementation specific to industrial robots.

In the main() function, we create pointers to objects of both the base class (Robot) and the derived class (IndustrialRobot). 

We then call the introduce() function on each object using the pointer.

Run the code.

4-function-overriding

You should see the output showing the different introduction messages for the base class and the derived class.

This example demonstrates how function overriding allows derived classes to provide their own implementation of functions defined in the base class. By marking the base class function as virtual, we enable polymorphic behavior, allowing the appropriate function to be called based on the actual object type at runtime.

Thanks, and I’ll see you in the next tutorial.

Keep building!