3.Compilation, Functions, Source, Library and Cmake
29 May 2020 | Modern C++
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
3. Link libraries when building code:
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
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
3. Link libraries when building code:
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