How to Generate Doxygen Comments for C++ Code

In this tutorial, we will learn how to generate Doxygen comments in C++ code using Visual Studio Code. Doxygen comments are essential for creating clear, professional documentation for your robotics code.

Prerequisites

Directions

Open a terminal window, and type this:

cd ~/Documents/cpp_tutorial
code .

Click the Extensions icon in the sidebar.

Search for “Doxygen Documentation Generator“.

Install the extension by Christoph Schlosser.

1-doxygen-documentation-generator-christoph-schlosser

Create a new file, and save it as robot_controller_doxygen.cpp. This will be our working file for this tutorial.

Type the following code into the editor:

#include <string>

class RobotController {
    private:
        double maxSpeed;
        std::string robotName;

    public:
        RobotController(std::string name, double speed);
        void moveForward(double distance);
};

To generate Doxygen comments, place your cursor above the class or function you want to document and type /** followed by Enter. The extension will automatically generate a comment template. Fill it in like this:

#include <string>

/**
 * @brief Controller class for robot movement
 * @details Handles basic movement operations and speed control for the robot
 */
class RobotController {
    private:
        double maxSpeed;
        std::string robotName;

    public:
        /**
         * @brief Construct a new Robot Controller object
         * @param name The name identifier for the robot
         * @param speed The maximum speed in meters per second
         */
        RobotController(std::string name, double speed);

        /**
         * @brief Moves the robot forward by the specified distance
         * @param distance The distance to move in meters
         * @return void
         */
        void moveForward(double distance);
};

Understanding the Code

Doxygen comments use special tags that begin with @ or \:

  • @brief provides a short description
  • @details gives a more detailed explanation
  • @param documents a parameter
  • @return describes the return value

The extension automatically generates the appropriate tags based on the code it’s documenting. You just need to fill in the descriptions.

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

Keep building!

How to Implement Object-Oriented Programming Basics in C++

In this tutorial, we will explore object-oriented programming basics in C++.

Prerequisites

Implementing Classes and Objects

In this tutorial, we’re going to enhance our understanding of object-oriented programming in C++ by creating a class to represent a robotic distance sensor. Sensors are pivotal in robotics, providing the ability to perceive and interact with the environment.

Open a terminal window, and type this:

cd ~/Documents/cpp_tutorial
code .

Let’s begin by creating a new C++ file and naming it distance_sensor.cpp.

Type the following code into the editor:

#include <iostream>
using namespace std;

// Definition of the DistanceSensor class
class DistanceSensor {
private:
    double range;  // Maximum range of the sensor in meters

public:
    // Constructor that initializes the sensor's range
    DistanceSensor(double max_range) : range(max_range) {}

    // Method to display the maximum range of the sensor
    void displayRange() {
        cout << "Sensor maximum range: " << range << " meters" << endl;
    }
};

int main() {
    // Creating an instance of DistanceSensor with a range of 1.5 meters
    DistanceSensor front_sensor(1.5);
    front_sensor.displayRange();  // Calling the display method
    return 0;
}

Here’s a breakdown of what we just wrote:

#include <iostream> allows us to use input and output operations, specifically cout for displaying text.

We declare a class named DistanceSensor that models a distance sensor in a robotic system. It includes:

  • A private data member range, which is not accessible outside the class. This encapsulation is a key principle of object-oriented programming, protecting the integrity of the data.
  • A public constructor that initializes the range when a new object is created. The initializer list (: range(max_range)) directly sets the range member variable.
  • A public method displayRange() that outputs the range to the console. This method demonstrates how objects can have behaviors through functions.

Run the code.

1-distance-sensor

This example shows how to define a class with private data and public methods, encapsulating the functionality in a way that’s easy to manage and expand for larger robotic systems. Using classes like this helps keep your robot’s code organized and modular.

Using Header Files

Let’s learn how to use header files in C++ to organize our code. Whether you’re building a small project or a complex robotics system, understanding header files is important.

Header files (typically ending in .hpp or .h) serve as a “contract” or “interface” for your code. They declare what functionality is available without specifying how that functionality is implemented. This separation between declaration and implementation is a key principle in C++ programming.

Let’s create a minimal example with two files:

  1. robot.hpp – Our header file containing the class declaration
  2. robot.cpp – Our implementation file containing the actual code and main function

Type the following code into robot.hpp:

#ifndef ROBOT_HPP_
#define ROBOT_HPP_

class Robot {
public:
    void greet();
};

#endif

Let’s break down what’s happening here:

  • #ifndef, #define, and #endif are called “include guards”. They prevent multiple inclusions of the same header file, which could cause compilation errors.
  • The class Robot is declared with a single public method greet().
  • Notice we only declare what the class can do, not how it does it.

Now type the following code into robot.cpp:

#include "robot.hpp"
#include <iostream>

void Robot::greet() {
    std::cout << "Hello, I am a robot." << std::endl;
}

int main() {
    Robot my_robot;
    my_robot.greet();
    return 0;
}

Here’s what’s happening in our implementation file:

  • #include “robot.hpp” tells the compiler to insert the contents of our header file here.
  • #include <iostream> gives us access to input/output functionality.
  • We implement the greet() method using Robot::greet() syntax to specify it belongs to the Robot class.
  • The main() function creates a robot object and calls its method.

When you use #include, the compiler looks for the specified file in different locations depending on how you include it:

  • #include “file.hpp” (with quotes): Searches first in the current directory, then in compiler-specified include paths
  • #include <file.hpp> (with angles): Searches only in compiler-specified system include paths (e.g. /usr/include)

Run the robot.cpp code using Code Runner.

You could also run the code as follows…

Open a terminal in Visual Studio Code by navigating to ‘Terminal’ in the top menu and clicking on ‘New Terminal’. Type the following command to compile:

g++ robot.cpp -o robot

This command compiles robot.cpp, and because robot.hpp is in the same directory and included in robot.cpp, the compiler finds it without issue. 

If robot.hpp were in another directory, you would need to tell the compiler where to find it using the -I option followed by the path to the directory.

Run the program with:

./robot
2-using-header-files

Defining Access Specifiers: Private, Protected, and Public

Let’s explore access specifiers in C++, which are important for object-oriented programming in robotics applications. Access specifiers determine the visibility and accessibility of class members, ensuring proper encapsulation and data protection.

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

Type the following code into the editor:

#include <iostream>

class Robot {
private:
    int battery_level_; // Only accessible within the class

protected:
    int max_speed_; // Accessible within the class and derived classes

public:
    Robot(int battery, int speed) {
        battery_level_ = battery;
        max_speed_ = speed;
    }

    void print_status() {
        std::cout << "Battery Level: " << battery_level_ << std::endl;
        std::cout << "Max Speed: " << max_speed_ << std::endl;
    }
};

int main() {
    Robot my_robot(75, 10);
    my_robot.print_status(); // Allowed as print_status() is public
    return 0;
}

In this example, we define a Robot class with three members:

  • battery_level_ (private): Only accessible within the Robot class itself.
  • max_speed_ (protected): Accessible within the Robot class and any classes derived from it.
  • print_status() (public): Accessible from anywhere, including outside the class.

The private specifier ensures that the battery_level_ variable cannot be accessed or modified directly from outside the class, promoting data encapsulation and preventing unintended modifications.

The protected specifier allows the max_speed_ variable to be accessed by the Robot class and any classes derived from it, facilitating code reuse and inheritance in robotics applications.

The public specifier makes the print_status() function accessible from anywhere, allowing other parts of the program to retrieve and display the robot’s status.

Run the code.

You should see the robot’s battery level and max speed printed in the terminal.

3-access-specifiers

Employing the static Keyword

Let’s explore the static keyword in C++ and how it can be useful in robotics projects.

The static keyword in C++ has two primary uses:

  1. Static variables: When a variable is declared as static inside a function, it retains its value between function calls.
  2. Static member variables and functions: When a member variable or function is declared as static in a class, it belongs to the class itself rather than any specific instance of the class.

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

Type the following code into the editor:

#include <iostream>

void increment_counter() {
    static int count = 0;
    count++;
    std::cout << "Counter: " << count << std::endl;
}

int main() {
    for (int i = 0; i < 5; i++) {
        increment_counter();
    }
    return 0;
}

In this code, we define a function called increment_counter() that increments a static variable count each time it is called. The count variable is initialized to 0 only once, and its value persists between function calls.

In the main() function, we call increment_counter() five times using a for loop.

Run the code.

4-static-example

You should see the value of count increasing with each function call, demonstrating that the static variable retains its value between calls.

This example illustrates how static variables can be useful in robotics projects. For instance, you might use a static variable to keep track of the total distance traveled by a robot or to count the number of objects a robot has picked up.

Static member variables and functions are also valuable in robotics, as they allow you to define properties and behaviors that are shared by all instances of a class, such as a robot’s maximum speed or a function that calculates the inverse kinematics for a robotic arm.

Implementing Constructors

Let’s learn about constructors in C++ and how they can be used in robotics projects.

Constructors are special member functions in C++ that are automatically called when an object of a class is created. They are used to initialize the object’s data members and perform any necessary setup.

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

Type the following code into the editor:

#include <iostream>
#include <string>

class Robot {
public:
    Robot(std::string name, int x, int y) {
        name_ = name;
        x_ = x;
        y_ = y;
        std::cout << "Robot " << name_ << " created at position (" << x_ << ", " << y_ << ")" << std::endl;
    }

private:
    std::string name_;
    int x_;
    int y_;
};

int main() {
    Robot robot1("AutomaticAddisonBot1", 0, 0);
    Robot robot2("AutomaticAddisonBot2", 10, 20);
    return 0;
}

In this code, we define a Robot class with a constructor that takes three parameters: name, x, and y. The constructor initializes the name_, x_, and y_ data members of the class and prints a message indicating the robot’s name and initial position.

In the main() function, we create two Robot objects, robot1 and robot2, with different names and initial positions.

Run the code.

5-constructor-example

You should see messages in the terminal indicating the creation of the two robots with their respective names and initial positions.

This example demonstrates how constructors can be used to initialize objects in a robotics project. You can use constructors to set up a robot’s initial state, such as its position, orientation, or any other relevant parameters.

Overloading Functions

Let’s explore function overloading in C++ and how you can apply it in your robotics projects.

Function overloading is a feature in C++ that allows you to define multiple functions with the same name but different parameter lists. The compiler distinguishes between the overloaded functions based on the number, types, and order of the arguments passed when the function is called.

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

Type the following code into the editor:

#include <iostream>

void move_robot(int distance) {
    std::cout << "Moving robot forward by " << distance << " units" << std::endl;
}

void move_robot(int x, int y) {
    std::cout << "Moving robot to position (" << x << ", " << y << ")" << std::endl;
}

int main() {
    move_robot(10);
    move_robot(5, 7);
    return 0;
}

In this code, we define two functions named move_robot. The first function takes a single integer parameter distance, while the second function takes two integer parameters x and y.

The first move_robot function simulates moving the robot forward by a specified distance, while the second move_robot function simulates moving the robot to a specific position on a 2D plane.

In the main() function, we call both overloaded move_robot functions with different arguments.

Run the code.

6-overloading-example

You should see messages in the terminal indicating the robot’s movement based on the arguments passed to the overloaded move_robot functions.

Function overloading is particularly useful when you want to provide multiple ways to perform a similar action, such as moving a robot, but with different parameters or units of measurement.

Implementing Destructors

Let’s learn about destructors in C++ and how you can use them in robotics projects.

Destructors are special member functions in C++ that are automatically called when an object of a class is destroyed. They are used to clean up any resources allocated by the object during its lifetime, such as memory, file handles, or network connections.

Create a new C++ file, and name it destructor_example.cpp.

Type the following code into the editor:

#include <iostream>

class RobotController {
public:
    RobotController() {
        std::cout << "Robot controller initialized" << std::endl;
    }

    ~RobotController() {
        std::cout << "Robot controller shutting down" << std::endl;
        // Clean up resources, e.g., close connections, release memory
    }

    void control_robot() {
        std::cout << "Controlling robot..." << std::endl;
    }
};

int main() {
    RobotController controller;
    controller.control_robot();
    return 0;
}

In this code, we define a RobotController class with a constructor and a destructor. The constructor is called when a RobotController object is created, and it prints a message indicating that the controller has been initialized.

The destructor is prefixed with a tilde (~) and has the same name as the class. It is called when the RobotController object goes out of scope or is explicitly deleted. 

In this example, the destructor prints a message indicating that the controller is shutting down. In a real-world scenario, the destructor would also clean up any resources allocated by the controller.

The control_robot() function simulates the controller performing some robot control tasks.

In the main() function, we create a RobotController object named controller and call its control_robot() function.

Run the code.

7-destructor-example

You should see messages in the terminal indicating the initialization of the robot controller, the control of the robot, and finally, the shutting down of the controller when the object is destroyed.

This example demonstrates how destructors can be used in robotics projects to ensure proper cleanup and resource management. Destructors are particularly important when working with limited resources or when managing external connections, such as communication with hardware or other systems.

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

Keep building!

How to Use Functions and Pointers in C++

In this tutorial, we will explore functions and pointers in C++.

Prerequisites

Using Mathematical Functions

Let’s explore how to use mathematical functions in C++ for robotics. Mathematical functions are essential for performing various calculations in robotic applications.

Open a terminal window, and type this:

cd ~/Documents/cpp_tutorial

code .

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

Type the following code into the editor:

#include <iostream>
#include <cmath>

using namespace std;

int main() {
    double angle = 45.0;
    double radians = angle * M_PI / 180.0;

    double sine = sin(radians);
    double cosine = cos(radians);

    cout << "Sine: " << sine << endl;
    cout << "Cosine: " << cosine << endl;

    return 0;
}

In this example, we demonstrate how to use mathematical functions to calculate the sine and cosine of an angle.

First, we include the <cmath> header to use the mathematical functions. Then, we declare a double variable angle and assign it the value 45.0, representing an angle in degrees.

To convert the angle from degrees to radians, we multiply it by M_PI (which represents the mathematical constant pi) and divide by 180.0. We store the result in the radians variable.

To calculate the sine and cosine of the angle, we use the sin() and cos() functions, respectively. These functions expect the angle to be in radians. 

We pass the radians variable as an argument to these functions and store the results in the sine and cosine variables.

Finally, we print the values of sine and cosine using cout.

Run the code.

1-robot-math-functions

You should see the values of sine and cosine printed in the terminal.

In robotic projects, mathematical functions are commonly used for tasks such as calculating robot positions, orientations, sensor data processing, control algorithms, and motion planning.

Implementing Functions

Let’s explore how to implement functions in C++ for robotics. Functions are essential for organizing and reusing code in robotic applications.

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

Type the following code into the editor:

#include <iostream>
#include <cmath>  // Added header for sqrt()
using namespace std;

// Function declaration 
double calculate_distance(double x1, double y1, double x2, double y2);

int main() {
    double distance = calculate_distance(0, 0, 3, 4);
    cout << "Distance: " << distance << endl;
    return 0;
}

// Function definition
double calculate_distance(double x1, double y1, double x2, double y2) {
    double dx = x2 - x1;
    double dy = y2 - y1;
    double distance = sqrt(dx * dx + dy * dy);
    return distance;
}

In this example, we demonstrate how to implement a function to calculate the distance between two points.

First, we declare the calculate_distance function before the main function. The function takes four parameters: x1, y1, x2, and y2, representing the coordinates of two points. It returns a double value, which is the calculated distance.

In the main function, we call the calculate_distance function with the coordinates (0, 0) and (3, 4). The returned distance is stored in the distance variable and then printed using cout.

After the main function, we provide the function definition for calculate_distance. Inside the function, we calculate the differences in x and y coordinates (dx and dy). 

Then, we use the distance formula (Pythagorean theorem) to calculate the distance between the points. 

Finally, we return the calculated distance.

Run the code.

2-robot-functions

You should see the calculated distance printed in the terminal.

In robotic projects, you can use functions for various purposes, such as calculating sensor data, controlling robot movements, implementing algorithms, and more.

Handling Pointers

Let’s explore how to handle pointers in C++ for robotics. Pointers are variables that store memory addresses and allow you to manipulate data directly in memory.

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

Type the following code into the editor:

#include <iostream>

using namespace std;

int main() {
    int robot_id = 42;
    int* ptr = &robot_id;

    cout << "Robot ID: " << robot_id << endl;
    cout << "Pointer Value: " << ptr << endl;
    cout << "Dereferenced Pointer: " << *ptr << endl;

    *ptr = 99;
    cout << "Updated Robot ID: " << robot_id << endl;

    return 0;
}

In this example, we demonstrate how to handle pointers to manipulate data in memory.

First, we declare an integer variable robot_id and assign it the value 42. Then, we declare a pointer variable ptr and initialize it with the address of robot_id using the address-of operator &. The & operator retrieves the memory address of a variable.

We print the value of robot_id, the value of ptr (which is the memory address), and the dereferenced value of ptr using the dereference operator *. The * operator, when used in front of a pointer variable, retrieves the value stored at the memory address pointed to by the pointer. This is called dereferencing.

Next, we use the dereference operator * to modify the value at the memory address pointed to by ptr. 

We assign the value 99 to *ptr, which effectively updates the value of robot_id. By dereferencing ptr and assigning a new value, we are changing the value stored at the memory address pointed to by ptr, which is the memory address of robot_id.

Finally, we print the updated value of robot_id to confirm that it has been modified through the pointer.

Run the code.

3-robot-pointers

You should see the original robot ID, the pointer value (memory address), the dereferenced pointer value (which is the same as the original robot ID), and the updated robot ID printed in the terminal.

In robotics projects, pointers allow you to directly access and modify data without needing to move or copy it, which makes programs run faster and use less memory.

Managing Exceptions

Let’s learn how to manage exceptions in C++ for robotics applications. Proper exception handling is important for robust and reliable software systems, especially in the field of robotics where errors can have significant consequences.

Let’s start by creating a new C++ file called exception_handling.cpp.

Type the following code into the editor:

#include <iostream>
#include <stdexcept>

double divide(double a, double b) {
    if (b == 0) {
        throw std::runtime_error("Division by zero");
    }
    return a / b;
}

int main() {
    try {
        double result = divide(10, 0);
        std::cout << "Result: " << result << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    return 0;
}

In this example, we define a function divide that throws a std::runtime_error exception if the denominator is zero. In the main function, we wrap the call to divide in a try block and handle any potential exceptions in the catch block.

Run the code.

4-exception-handling

You should see the error message “Error: Division by zero” printed in the terminal, as we intentionally passed 0 as the second argument to the divide function.

Proper exception handling is important in robotics applications, where unexpected situations or sensor failures can occur. By using exceptions and handling them appropriately, you can ensure that your code gracefully handles errors and maintains a consistent state, preventing potential damage or safety issues.

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

Keep building!