for Robot Artificial Inteligence

4. Google tests, Namespaces, Classes

|

Use GTest(Google Test) to test your functions

  • Catch bugs early to fix them with less pain
  • Testing is crucial to catch bugs early
  • Tested functions are easier to trust
  • For every function write at least two tests
    • One for normal cases
    • One for extreme cases
  • Make writing tests a habit

How do tests look?

  • A single dummy Google test:
    TEST(TestModule , FunctionName ) {
    EXPECT_EQ (4, FunctionName ());
    }
    
  • Successful output:

Add GTests with CMake

  • Install GTest source files (build them later):
    • sudo apt install libgtest-dev
  • Add folder tests to your CMake project:
    # Must be in the top-most CMakeLists.txt file
    enable_testing ()
    # Outsource tests to another folder
    add_subdirectory(tests)
    

Configure tests

# Add gtest sources folder. Provides gtest , gtest_main.
add_subdirectory(/usr/src/gtest
                ${PROJECT_BINARY_DIR}/gtest)
include(CTest) # Include testing cmake package.
# Set binary name for convenience.
set( TEST_BINARY ${PROJECT_NAME}_test)
# This is an executable that runs the tests.
add_executable(${TEST_BINARY} test_tools.cpp)
# Link the executable to needed libraries.
target_link_libraries(${TEST_BINARY}
  tools # Library we are testing
  gtest gtest_main # GTest libraries
)
# Add gtest to be able to run ctest
add_test(
  NAME ${TEST_BINARY}
  COMMAND ${EXECUTABLE_OUTPUT_PATH}/${TEST_BINARY})

Run your tests

  • Build your code just like before
  • Add one additional step after building
    cd <project_folder>
    mkdir build
    cd build
    cmake ..
    make
    ctest -VV
    

Namespaces

  • Helps avoiding name conflicts
  • Group the project into logical modules

Namespaces example

#include <iostream>

namespace boring{
  int GetMeaningOfLife() {return 0;}
}

namespace fun{
  int GetMeaningOfLife() {return 42;}
}

int main () {
  std :: cout << "The answer to everything is not "
              << boring :: GetMeaningOfLife () << " but "
              << fun :: GetMeaningOfLife () << std :: endl;
  return 0;
}

Avoid using namespace

#include <cmath >
#include <iostream >
using namespace std; // std namespace is used
// Self -defined function power shadows std::pow
double pow(double x, int exp) {
  double res = 1.0;
  for (int i = 0; i < exp; i++) { res * = x; }
  cout << "Our cool power function\n";
  return (res);
}
int main () {
  double x = 2.0;
  int power = 2;
  double res = pow(x, power);
  cout << x << " ^ " << power << " = " << res << endl;
  return 0;
}

Namespace error

Only use what you need

#include <cmath >
#include <iostream >
using std::cout; // Explicitly use cout, std 네임스페이스에서 오직 cout만 쓰게한다
using std::endl; // Explicitly use cout, std 네임스페이스에서 오직 endl만 쓰게한다
// Self -defined function power shadows std::pow
double pow(double x, int exp) {
  double res = 1.0;
  for (int i = 0; i < exp; i++) { res * = x; }
  cout << "Our cool power function\n";
  return (res);
}
int main () {
  double x = 2.0;
  int power = 2;
  double res = pow(x, power);
  cout << x << " ^ " << power << " = " << res << endl;
  return 0;
}

Namespaces Wrap Up

Use namespaces to avoid name conflicts

namespace some_name {
  <your_code >
  } // namespace some_name

Use using correctly

  • [good]
    • using my_namespace::myFunc;
    • my_namespace::myFunc(…);
  • Never use using namespace name in * .h files
  • Prefer using explicit using even in * .cpp files

Nameless namespaces

  • If you find yourself relying on some contstants in a file and these constants should not be seen in any other file, put them into a nameless namespace on the top of this file(Local variable of certain file)
namespace {
  const int kLocalImportantInt = 13;
  const float kLocalImportantFloat = 13.0f;
}

Create new types with classes and structs

  • Classes are used to encapsulate data along with methods to process them
  • Every class or struct defines a new type
  • Terminology:
    • Type or class to talk about the defined type
    • A variable of such type is an instance of class or an object
  • asses allow C++ to be used as an Object Oriented Programming language
  • string, vector, etc. are all classes

Example Class definition

class Image{
  public:
    Image(const std::string& file_name);
    void Draw();
  private:
    int rows_ = 0;
    int cols_ = 0;
}
int main () {
  Image image("some_image.pgm");
  image.Draw ();
  return 0;
}

Classes syntax

  • Definition starts with the keyword class
  • Classes have three access modifiers: private, protected and public
  • By default everything is private
  • Classes can contain data and functions
  • Access members with a “.”
  • Have two types of special functions:
    • Contructors: called upon creation of an instance of the class in stack memory
    • Destructor: called upon destruction of an instance of the class in stack memory
  • GOOGLE-STYLE Use CamelCase for class name

What about structs?

  • Definition starts with the keyword struct:
    struct ExampleStruct {
    Type value;
    Type value;
    Type value;
    // No functions!
    };
    
  • struct is a class where everything is public
  • GOOGLE-STYLE Use struct as a simple data container, if it needs a function it should be a class instead

Always initialize structs using braced initialization

#include <iostream>
#include <string>
using namespace std;
// Define a structure.
struct NamedInt {
  int num;
  string name;
};
void PrintStruct (const NamedInt& s) {
  cout << s.name << " " << s.num << endl;
}
int main(int argc , char const* argv []) {
  NamedInt var = {1, "hello"};
  PrintStruct (var);
  PrintStruct ({10 , "world"});
  return 0;
}

Data stored in a class

  • Classes can store data of any type
  • GOOGLE-STYLE All data must be private
  • GOOGLE-STYLE Use snake_case_ with a trailing “_ “ for private data members
  • Data should be set in the Constructor
  • Cleanup data in the Destructor if needed

Constructors and Destructor

  • Classes always have at least one Constructor and exactly one Destructor
  • Constructors crash course:
    • Are functions with no return type
    • Named exactly as the class
    • There can be many constructors
    • If there is no explicit constructor an implicit default constructor will be generated
  • Destructor for class SomeClass:
    • Is a function named ~SomeClass()
    • Last function called in the lifetime of an object
    • Generated automatically if not explicitly defined

Many ways to create instances

class SomeClass {
  public:
    SomeClass (); // Default constructor.
    SomeClass (int a); // Custom constructor.
    SomeClass (int a, float b); // Custom constructor.
    ~ SomeClass (); // Destructor.
};
// How to use them?
 int main () {
   SomeClass var_1; // Default constructor
   SomeClass var_2 (10); // Custom constructor
   // Type is checked when using {} braces. Use them!
   SomeClass var_3 {10}; // Custom constructor
   SomeClass var_4 = {10}; // Same as var_3
   SomeClass var_5 {10, 10.0}; // Custom constructor
   SomeClass var_6 = {10, 10.0}; // Same as var_5
   return 0;
 }

Setting and getting data

  • Use initializer list to initialize data
  • Name getter functions as the private member they return
  • Make getters const
  • Avoid setters, set data in the constructor
class Student {
  public:
    Student(int id , string name): id_{id}, name_{name} {} //initilized
    int id() const { return id_; }//input parameter convert into data variable of class
    const string& name () const { return name_; }
  private:
    int id_;
    string name_;

Const correctness

  • const after function states that this function does not change the object
  • Mark all functions that should not change the state of the object as const
  • Ensures that we can pass objects by a const reference and still call their functions
  • Substantially reduces number of errors

Typical const error

Declaration and definition

  • Data members belong to declaration
  • Class methods can be defined elsewhere
  • Class name becomes part of function name
// Declare class.
class SomeClass {
  public:
    SomeClass ();
    int var () const;
  private:
    void DoSmth ();
    int var_ = 0;
};
// Define all methods.
SomeClass :: SomeClass () {}
int SomeClass :: var () const { return var_; }
void SomeClass :: DoSmth () {}

Always initialize members for classes

  • C++11 allows to initialize variables in-place
  • Do not initialize them in the constructor
  • No need for an explicit default constructor
    class Student {
    public:
      // No need for default constructor.
      // Getters and functions omitted.
    private:
      int earned_points_ = 0;
      float happiness_ = 1.0f;
    };
    
  • Note: Leave the members of structs uninitialized as defining them forbids using brace initialization

Classes as modules

  • Prefer encapsulating information that belongs together into a class
  • Separate declaration and definition of the class into header and source files
  • Typically, class SomeClass is declared in some_class.h and is defined in some_class.cpp

Reference

https://www.ipb.uni-bonn.de/teaching/modern-cpp/

Cpp Core Guidelines: https://github.com/isocpp/CppCoreGuidelines

Git guide: http://rogerdudler.github.io/git-guide/

C++ Tutorial: http://www.cplusplus.com/doc/tutorial/

Book: Code Complete 2 by Steve McConnell

Modern CMake Tutorial https://www.youtube.com/watch?v=eC9-iRN2b04

Compiler Explorer: https://godbolt.org/ Gdbgui: https://www.gdbgui.com/ CMake website: https://cmake.org/ Gdbgui tutorial: https://www.youtube.com/watch?v=em842geJhfk

Comments