Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
C++ Fundamentals

You're reading from   C++ Fundamentals Hit the ground running with C++, the language that supports tech giants globally

Arrow left icon
Product type Paperback
Published in Mar 2019
Publisher
ISBN-13 9781789801491
Length 350 pages
Edition 1st Edition
Languages
Arrow right icon
Authors (2):
Arrow left icon
 Mallia Mallia
Author Profile Icon Mallia
Mallia
 Zoffoli Zoffoli
Author Profile Icon Zoffoli
Zoffoli
Arrow right icon
View More author details
Toc

Table of Contents (8) Chapters Close

About the Book 1. Getting Started FREE CHAPTER 2. Functions 3. Classes 4. Generic Programming and Templates 5. Standard Library Containers and Algorithms 6. Object-Oriented Programming 1. Appendix

Lesson 04: Generic Programming and Templates

Activity 13: Read Objects from a Connection

  1. We start by including the headers of the files that provided the connection and the user account object:

    #include <iostream>

    #include <connection.h>

    #include <useraccount.h>

  2. We can then start to write the writeObjectToConnection function. Declare a template which takes two typename parameters: an Object and a Connection. Call the static method serialize() on the object to get the std::array representing the object, then call writeNext() on the connection to write the data to it:

    template<typename Object, typename Connection>

    void writeObjectToConnection(Connection& con, const Object& obj) {

        std::array<char, 100> data = Object::serialize(obj);

        con.writeNext(data);

    }

  3. We can then write readObjectFromConnection. Declare a template taking the same two parameters as before: an Object and a Connection. Inside, we call the connection readNext() to get the data stored inside the connection, then we call the static method on the object type deserialize() to get an instance of the object and return it:

    template<typename Object, typename Connection>

    Object readObjectFromConnection(Connection& con) {

        std::array<char, 100> data = con.readNext();

        return Object::deserialize(data);

    }

  4. Finally, in the main function, we can call the functions we created to serialize objects. Both with TcpConnection:

    std::cout << “serialize first user account” << std::endl;

    UserAccount firstAccount;

    TcpConnection tcpConnection;

    writeObjectToConnection(tcpConnection, firstAccount);

    UserAccount transmittedFirstAccount = readObjectFromConnection<UserAccount>(tcpConnection);

  5. And with UdpConnection:

    std::cout << “serialize second user account” << std::endl;

    UserAccount secondAccount;

    UdpConnection udpConnection;

    writeObjectToConnection(udpConnection, secondAccount);

    UserAccount transmittedSecondAccount = readObjectFromConnection<UserAccount>(udpConnection);

    The output of the program is as follows:

    serialize first user account

    the user account has been serialized

    the data has been written

    the data has been read

    the user account has been deserialized

    serialize second user account

    the user account has been serialized

    the data has been written

    the data has been read

    the user account has been deserialized

Activity 14: UserAccount to Support Multiple Currencies

  1. We start by including the file defining the currencies:

    #include <currency.h>

    #include <iostream>

  2. We then declare the template class Account. It should take a template parameter: Currency. We store the current balance of the account inside a data member of type Currency. We also provide a method in order to extract the current value of the balance:

    template<typename Currency>

    class Account {

      public:

        Account(Currency amount) : balance(amount) {}

        Currency getBalance() const {

            return balance;

        }

      private:

        Currency balance;

    };

  3. Next, we create the method addToBalance. It should be a template with one type parameter, the other currency. The method takes a value of OtherCurrency and converts it to the value of the currency of the current account with the to() function, specifying to which currency the value should be converted to. It then adds it to the balance:

    template<typename OtherCurrency>

    void addToBalance(OtherCurrency amount) {

        balance.d_value += to<Currency>(amount).d_value;

    }

  4. Finally, we can try to call our class in the main function with some data:

    Account<GBP> gbpAccount(GBP(1000));

    // Add different currencies

    std::cout << “Balance: “ << gbpAccount.getBalance().d_value << “ (GBP)” << std::endl;

    gbpAccount.addToBalance(EUR(100));

    std::cout << “+100 (EUR)” << std::endl;

    std::cout << “Balance: “ << gbpAccount.getBalance().d_value << “ (GBP)” << std::endl;

    The output of the program is:

    Balance: 1000 (GBP)

    +100 (EUR)

    Balance: 1089 (GBP)

Activity 15: Write a Matrix Class for Mathematical Operations in a Game

  1. We start by defining a Matrix class which takes three template parameters: one type and the two dimensions of the Matrix class. The dimensions are of type int. Internally, we create a std::array with the size of the number of rows times the number of columns, in order to have enough space for all elements of the matrix. We add a constructor to initialize the array to empty, and a constructor to provide a list of values:

    #include <array>

    template<typename T, int R, int C>

    class Matrix {

      // We store row_1, row_2, ..., row_C

      std::array<T, R*C> data;

      public:

        Matrix() : data({}) {}

        Matrix(std::array<T, R*C> initialValues) : data(initialValues) {}

    };

  2. We add a method get() to the class to return a reference to the element T. The method needs to take the row and column we want to access.
  3. We make sure that the requested indexes are inside the bounds of the matrix, otherwise we call std::abort(). In the array, we first store all the elements of the first row, then all the elements of the second row, and so on. When we want to access the elements of the nth row, we need to skip all the elements of the previous rows, which are going to be the number of elements per row (so the number of columns) times the previous rows, resulting in the following method:

    T& get(int row, int col) {

      if (row >= R || col >= C) {

        std::abort();

      }

      return data[row*C + col];

    }

  4. For convenience, we define a function to print the class as well. We print all the elements in the columns separated by spaces, with one column per line:

    template<typename T, size_t R, size_t C>

    std::ostream& operator<<(std::ostream& os, Matrix<T, R, C> matrix) {

        os << ‘\n’;

        for(int r=0; r < R; r++) {

            for(int c=0; c < C; c++) {

                os << matrix.get(r, c) << ‘ ‘;

            }

            os << “\n”;

        }

        return os;

    }

  5. In the main function, we can now use the functions we have defined:

    Matrix<int, 3, 2> matrix({

      1, 2,

      3, 4,

      5, 6

    });

    std::cout << “Initial matrix:” << matrix << std::endl;

    matrix.get(1, 1) = 7;

    std::cout << “Modified matrix:” << matrix << std::endl;

    The output is as follows:

    Initial matrix:

    1 2

    3 4

    5 6

    Modified matrix:

    1 2

    3 7

    5 6

Solution bonus step:

  1. We can add a new method, multiply, which takes a std::array of type T with the length of C by const reference, since we are not modifying it.

    The function returns an array of the same type, but length R.

  2. We follow the definition of matrix-vector multiplication to compute the result:

    std::array<T, R> multiply(const std::array<T, C>& vector){

        std::array<T, R> result = {};

        for(size_t r = 0; r < R; r++) {

          for(size_t c = 0; c < C; c++) {

            result[r] += get(r, c) * vector[c];

          }

        }

        return result;

    }

  3. We can now extend our main function to call the multiply function:

    std::array<int, 2> vector = {8, 9};

    std::array<int, 3> result = matrix.multiply(vector);

    std::cout << “Result of multiplication: [“ << result[0] << “, “

      << result[1] << “, “ << result[2] << “]” << std::endl;

    The output is as follows:

    Result of multiplication: [26, 87, 94]

Activity 16: Make the Matrix Class Easier to Use

  1. We start by importing <functional> in order to have access to std::multiplies:

    #include <functional>

  2. We then change the order of the template parameters in the class template, so that the size parameters come first. We also add a new template parameter, Multiply, which is the type we will use for computing the multiplication between the elements in the vector by default, and we store an instance of it in the class:

    template<int R, int C, typename T = int, typename Multiply=std::multiplies<T> >

    class Matrix {

      std::array<T, R*C> data;

      Multiply multiplier;

      public:

        Matrix() : data({}), multiplier() {}

        Matrix(std::array<T, R*C> initialValues) : data(initialValues), multiplier() {}

    };

    The get() function remains the same as the previous activity.

  3. We now need to make sure that the Multiply method uses the Multiply type provided by the user to perform the multiplication.
  4. To do so, we need to make sure to call multiplier(operand1, operand2) instead of operand1 * operand2, so that we use the instance we stored inside the class:

    std::array<T, R> multiply(const std::array<T, C>& vector) {

        std::array<T, R> result = {};

        for(int r = 0; r < R; r++) {

            for(int c = 0; c < C; c++) {

                result[r] += multiplier(get(r, c), vector[c]);

            }

        }

        return result;

    }

  5. We can now add an example of how we can use the class:

    // Create a matrix of int, with the ‘plus’ operation by default

    Matrix<3, 2, int, std::plus<int>> matrixAdd({

        1, 2,

        3, 4,

        5, 6

    });

    std::array<int, 2> vector = {8, 9};

    // This will call std::plus when doing the multiplication

    std::array<int, 3> result = matrixAdd.multiply(vector);

    std::cout << “Result of multiplication(with +): [“ << result[0] << “, “

              << result[1] << “, “ << result[2] << “]” << std::endl;

    The output is as follows:

    Result of multiplication(with +): [20, 24, 28]

Activity 17: Ensure Users are Logged in When Performing Actions on the Account

  1. We first declare a template function which takes two type parameters: an Action and a Parameter type.
  2. The function should take the user identification, the action and the parameter. The parameter should be accepted as a forwarding reference. As a first step, it should check if the user is logged in, by calling the isLoggenIn() function. If the user is logged in, it should call the getUserCart() function, then call the action passing the cart and forwarding the parameter:

    template<typename Action, typename Parameter>

    void execute_on_user_cart(UserIdentifier user, Action action, Parameter&& parameter) {

        if(isLoggedIn(user)) {

            Cart cart = getUserCart(user);

            action(cart, std::forward<Parameter>(parameter));

        } else {

            std::cout << “The user is not logged in” << std::endl;

        }

    }

  3. We can test how execute_on_user_cart works by calling it in the main function:

    Item toothbrush{1023};

    Item toothpaste{1024};

    UserIdentifier loggedInUser{0};

    std::cout << “Adding items if the user is logged in” << std::endl;

    execute_on_user_cart(loggedInUser, addItems, std::vector<Item>({toothbrush, toothpaste}));

    UserIdentifier loggedOutUser{1};

    std::cout << “Removing item if the user is logged in” << std::endl;

    execute_on_user_cart(loggedOutUser, removeItem, toothbrush);

    The output is as follows:

    Adding items if the user is logged in

    Items added

    Removing item if the user is logged in

    The user is not logged in

Activity 18: Safely Perform Operations on User Cart with an Arbitrary Number of Parameters

  1. We need to expand the previous activity to accept any number of parameters with any kind of ref-ness and pass it to the action provided. To do so, we need to create a variadic template.
  2. Declare a template function that takes an action and a variadic number of parameters as template parameters. The function parameters should be the user action, the action to perform, and the expanded template parameter pack, making sure that the parameters are accepted as forwarding references.
  3. Inside the function, we perform the same checks as before, but now we expand the parameters when we forward them to the action:

    template<typename Action, typename... Parameters>

    void execute_on_user_cart(UserIdentifier user, Action action, Parameters&&... parameters) {

        if(isLoggedIn(user)) {

            Cart cart = getUserCart(user);

            action(cart, std::forward<Parameters>(parameters)...);

        } else {

            std::cout << “The user is not logged in” << std::endl;

        }

    }

  4. Let’s test the new function in our main function:

    Item toothbrush{1023};

    Item apples{1024};

    UserIdentifier loggedInUser{0};

    std::cout << “Replace items if the user is logged in” << std::endl;

    execute_on_user_cart(loggedInUser, replaceItem, toothbrush, apples);

    UserIdentifier loggedOutUser{1};

    std::cout << “Replace item if the user is logged in” << std::endl;

    execute_on_user_cart(loggedOutUser, removeItem, toothbrush);

    The output is as follows:

    Replace items if the user is logged in

    Replacing item

    Item removed

    Items added

    Replace item if the user is logged in

    The user is not logged in

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at £13.99/month. Cancel anytime
Visually different images