for Robot Artificial Inteligence

3.Compilation, Functions, Source, Library and Cmake

|

Compilation flags

  • There is a lot of flags that can be passed while compiling the code
    • -std=c++11, -o, etc

Other useful options:

  • Enable all warnings, treat them as errors:
    • -Wall, -Wextra, -Werror
  • Optimization options:
    • -O0 - no optimizations
    • -O3 or -Ofast - full optimizations(Best)
  • Keep debugging symbols: -g

Debugging tools

  • The best option is to use gdb
chan@chan~acer:/temp$ c++ -std=c++11 -g test.cpp
chan@chan~acer:/temp$ gdb a.out

Quit anyway? (y or n) y
chan@chan~acer:/temp$ gdb a.out
  • Insanely popular and powerful
  • No build-in gui
  • Use gdbgui for a user-friendly interface
  • Install gdbgui from pip:
    • sudo pip3 install –upgrade gdbgui

Functions

ReturnType FuncName(ParamType1 in_1 , ParamType2 in_2){
  // Some awesome code here.
  return return_value;
}
  • Code can be organized into functions
  • Functions create a scope
  • Single return value from a function
  • Any number of input variables of any types
  • Should do only one thing and do it right
  • Name must show what the function does
  • GOOGLE-STYLE name functions in CamelCase
  • GOOGLE-STYLE write small functions

Example in GOOGLE-STYLE Function

#include <vector>
using namespace std;
vector<int> CreateVectorOfFullSquares(int size){ // vecotr<int>는 데이터 타입이다. 모든 펑션에는 데이터 타입이 필요하다 like void, int, float, double 등등
  vector<int> result(size);
  for (int i=0; i<size; ++i) { result[i] = i * i; }
  return result;
}
int main(){
  auto squares = CreateVectorOfFullSquares(10);
  return 0;
}

  • Is small enough to see all the code at once
  • Name clearly states what the function does
  • Function does a single thing

Declaration and definition

  • Function declaration can be separated from the implementation details
  • Function declaration sets up an interface
void FuncName(int param);
  • Function definition holds the implementation of the function that can even be hidden from the user

void FuncName(int param){
  // Implementation details.
  cout<<"This function is called FuncName! ";
  cout<<"Did you expect anything useful from it?";
}

Passing big objects

  • By default in C++, objects are copied when passed into functions
  • If objects are big it might be slow
  • Pass by reference to avoid copy

void DoSmth(std::string huge_string); // Slow.
void DoSmth(std::string& huge_string); // Faster.

  • Is the string still the same?

string hello="some_important_long_string";
DoSmth(hello);

  • Unknown without looking into DoSmth()!

    Pass by reference Intuition

Pass by reference:

  • void fillCup(Cup &cup);
  • cup is full

    Pass by value:

  • void fillCup(Cup cup);
  • A copy of cup is full
  • cup is still empty

Example


#include <iostream>
using namespace std;
int sum(int& a, int b)
{
  a = 10;
  return a + b;
}
int main()
{
  int blah = 1;
  int a = sum(blah,2);
  cout<<a<<endl;
  return 0;
}
// output 12

Solution : use const references

  • 그냥 Reference만 붙여도 되지만 어차피 똑같은 결과 속도를 더 높이기 위해서 const int& 와 같은 데이터 타입에 const를 넣는다.
  • Pass const reference to the function
  • Great speed as we pass a reference
  • Passed object stays intact(完好)

void DoSmth(const std::string& huge_string);

  • Use snake_case for all function arguments
  • Non-const refs are mostly used in older code written before C++11
  • They can be useful but destroy readability
  • GOOGLE-STYLE Avoid using non-const refs

Function overloading

  • Compiler infers( 暗示) a function from arguments
  • Cannot overload based on return type
  • Return type plays no role at all
  • GOOGLE-STYLE Avoid non-obvious overloads

#include <iostream>
#include <string>
using namespace std;
string Func(int num) { return "int"; }
string Func(const string& str) { return "string"; }
int main (){
  cout<<Func(1)<< endl;
  cout<<Func("hello")<< endl;
  return 0;
}
// output int, hello

default arguments

  • Functions can accept default arguments
  • Only set in declaration not in definition
  • Pro: simplify function calls
  • Cons:
    • Evaluated upon every call
    • Values are hidden in declaration
    • Can lead to unexpected behavior when overused
  • GOOGLE-STYLE Only use them when readability gets much better

Example: default arguments


#include <iostream> // std::cout , std::endl
using namespace std;
string SayHello(const string& to_whom = "world") {
  return "Hello " + to_whom + "!";
}
int main () {
  cout<<SayHello()<< endl;
  cout<<SayHello("students")<<endl;
  return 0;
}

Don’t reinvent(以新形式出现) the wheel

  • When using std::vector, std::array, etc. try to avoid writing your own functions.
  • Use #include
  • There is a lot of functions in std which are at least as fast as hand-written ones:

std::vector<float> v;
// Filling the vector omitted here.
std::sort(v.begin (), v.end ()); // Sort ascending.
float sum = std::accumulate(v.begin(), v.end(), 0.0f);
float product = std::accumulate(v.begin(), v.end(), 1.0f, std::multiplies<float>());

Header / Source Separation

  • Move all declarations to header files (* .h)
  • implementation goes to * .cpp or * .cc

// some_file.h
Type SomeFunc(... args ...);

// some_file.cpp
#include "some_file.h"
Type SomeFunc(... args ...) { /* code * / }

// program.cpp
#include "some_file.h"
int main(){
  SomeFunc(/* args */);
  return 0;
}

How to build this?

folder/
    --- tools.h
    --- tools.cpp
    --- main.cpp

Short: we separate the code into modules

Declaration: tools.h


#pragma once // Ensure file is included only once
void MakeItSunny();
void MakeItRain();

  • #pragma once <- 중복 declaration을 방지

Definition: tools.cpp


#include <iostream>
#include "tools.h"
void MakeItRain(){
  // important weather manipulation code
  std::cout<<"Here! Now it rains! Happy?\n";
}
void MakeItSunny(){ std::cerr<<"Not available\n"; }

Calling : main.cpp


#include "tools.h"
int main (){
  MakeItRain();
  MakeItSunny();
  return 0;
}

Just build it as before?

c++ -std=c++11 main.cpp -o main

Error:

/tmp/tools_main -0 eacf5.o: In function `main ':
tools_main .cpp: undefined reference to `makeItRain ()'
tools_main .cpp: undefined reference to `makeItSunny ()'
clang: error: linker command failed with exit code 1
(use -v to see invocation )

Use modules and libraries

1. Compile modules:

c++ -std=c++11 -c tools.cpp -o tools.o

  • o is a compiled binary file for machine code(instrument) [ it called object files]

2. Organize modules into libraries:

ar rcs libtools.a tools.o

c++ -std=c++11 main.cpp -L . -ltools -o main

4. Run the Code:

./main

Libraries

  • Library: multiple object files that are logically connected
  • Types of libraries:
    • Static: faster, take a lot of space, become part of the end binary, named: lib*.a
    • Dynamic: slower, can be copied, referenced by a program, named lib*.so
  • Create a static library with
    • ar rcs libname.a module.o module.o …
  • Static libraries are just archives just like
    • zip/tar/…

What is linking?

  • The library is a binary object that contains the compiled implementation of some methods
  • Linking maps a function declaration to its compiled implementation
  • To use a library we need a header and the compiled library object

Use CMake to simplify the build

  • One of the most popular build tools
  • Does not build the code, generates files to eed into a build system
  • Cross-platform
  • Very powerful, still build receipt is readable
  • The library creation and linking can be rewritten as follows:
    add_library(tools tools.cpp)
    add_executable(main main.cpp)
    target_link_libraries(main tools)
    

Typical project structure

<a href=”https://postimg.cc/Jy2GgqgD>

Build process of CMAKE

  • CMakeLists.txt defines the whole build
  • CMake reads CMakeLists.txt sequentially
  • Build process:
    cd <project_folder>
    mkdir build
    cd build
    cmake ..
    make -j2 # pass your number of cores here
    

First working CMakeLists.txt

project( first_project ) # Mandatory.
cmake_minimum_required(VERSION 3.1) # Mandatory.
set( CMAKE_CXX_STANDARD 11) # Use c++11
# tell cmake to output binaries here:
set( EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
set( LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# tell cmake where to look for *.h files
include_directories(${PROJECT_SOURCE_DIR}/src)
# create library "libtools"
add_library(tools src/tools.cpp)
# add executable main
add_executable(main src/ tools_main .cpp)
# tell the linker to bind these objects together
target_link_libraries(main tools)

Useful commands in CMake

  • Just a scripting language
  • Has features of a scripting language, i.e.
    • functions, control structures, variables, etc.
  • All variables are string
  • Set variables with set(VAR VALUE)
  • Get value of a variable with ${VAR}
  • Show a message message(STATUS “message”)
  • Also possible WARNING, FATAL_ERROR

Use CMake in your builds

  • Build process is standard and simple
  • No need to remember sequence of commands
  • All generated build files are in one place
  • CMake detects changes to the files
  • Do this after changing files:
    cd project/build
    make -j2 # pass your number of cores here
    

Set compilation options in CMake

set ( CMAKE_CXX_STANDARD 14)
# Set build type if not set.
if(NOT CMAKE_BUILD_TYPE )
  set( CMAKE_BUILD_TYPE Release) //debug: we don't want optimization
endif ()
# Set additional flags.
set( CMAKE_CXX_FLAGS "-Wall -Wextra ")
set( CMAKE_CXX_FLAGS_DEBUG "-g -O0")
set( CMAKE_CXX_FLAGS_RELEASE "-O3")
  • -Wall -Wextra: show all warnings
  • -g: keep debug information in binary
  • -O: optimization level in {0, 1, 2, 3}
    • 0: no optimization
    • 3: full optimization

Remove build folder for performing a clean build

  • Sometimes you want a clean build
  • It is very easy to do with CMake
cd project/build
make clean [remove generated binaries]
rm -r * [make sure you are in build folder]

Use pre-compiled library

  • Sometimes you get a compiled library
  • You can use it in your build
  • For example, given libtools.so it can be used in the project as follows:
find_library(TOOLS
            NAMES tools
            PATHS ${LIBRARY_OUTPUT_PATH})
# Use it for linking:
target_link_libraries(<some_binary > ${TOOLS})

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