C++20(templates and concepts)

what is template value type?

value_type of the template is the type of template type
for example if you have std::vector<int> the value type is int like the example below …

template<typename T>
typename std::decay<typename T::value_type>::type fun2(T t){
    return *t.begin();
}

what is the “requires” keyword?

in before c++ 20 if you want to restrict type

template<class T>
class A{
public:
    using value_type=T;
};
template<class T>
class B:public A<T>{};
template<typename T,typename =std::enable_if_t<std::is_base_of_v<A<typename T::value_type>,T>>,
        typename =std::enable_if<is_arithmetic_v<typename T::value_type>>>
class tempClass{};
//tempClass<B<int>> correct
//tempClass<B<string>> is not correct string is not arithmetic type

but in c++20

template<typename T>
requires std::is_base_of_v<A<typename T::value_type>,T> && is_arithmetic_v<typename T::value_type>
class tempClass{};

what is the “concept” keyword?

template<class T>
concept MM=std::is_base_of_v<A<typename T::value_type>,T> && is_arithmetic_v<typename T::value_type>;


template<MM T>
class tempClass{};

//or

template<class T> requires MM<T>
class tempClass{};

C++ 20 (general subjects)

span array

void pspan(span<T> s) {
    cout << format("number of elements: {}\n", s.size());
    cout << format("size of span: {}\n", s.size_bytes());
    for(auto e : s) cout << format("{} ", e);
    cout << "\n";
}
int main() {
    int carray[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    pspan<int>(carray);
}

structure binding

int nums[] { 1, 2, 3, 4, 5 };
auto [ a, b, c, d, e ] = nums;
cout << format("{} {} {} {} {}\n", a, b, c, d, e);

array<int,5> nums { 1, 2, 3, 4, 5 };
auto [ a, b, c, d, e ] = nums;
cout << format("{} {} {} {} {}\n", a, b, c, d, e);

tuple<int, double, string> nums{ 1, 2.7, "three" };
auto [ a, b, c ] = nums;
cout << format("{} {} {}\n", a, b, c);

Initialize variables within if and switch statements

template<typename T>
const char * f(const T a) {
    return typeid(T).name();
}
int main() {
    cout << format("T is {}\n", f(47));
    cout << format("T is {}\n", f(47L));
    cout << format("T is {}\n", f(47.0));
    cout << format("T is {}\n", f("47"));
    cout << format("T is {}\n", f("47"s));
}

Use template argument deduction for simplicity and clarity

template<typename T>
const char * f(const T a) {
    return typeid(T).name();
}
int main() {
    cout << format("T is {}\n", f(47));
    cout << format("T is {}\n", f(47L));
    cout << format("T is {}\n", f(47.0));
    cout << format("T is {}\n", f("47"));
    cout << format("T is {}\n", f("47"s));
}
T is int
T is long
T is double
T is char const *

Use if constexpr to simplify compile-time decisions

template<typename T>
auto value_of(const T v) {
    if constexpr (std::is_pointer_v<T>) {
        return *v;  // dereference the pointer
    } else {
        return v;   // return the value
    }
}
int main() {
    int x{47};
    int* y{&x};
    cout << format("value is {}\n", value_of(x));  // value
    cout << format("value is {}\n", value_of(y));  
                                                // pointer
    return 0;
}

build GCC and G++

1- clone GCC repository

git clone git://gcc.gnu.org/git/gcc.git

2- add select branch

git branch -a #to list all branches
git checkout branch_name#to select wanted branch in our case it gcc-12

3- in the source folder you will find ./contrib/download_prerequisites  
execute it to download dependencies

4- call configuration https://gcc.gnu.org/install/configure.html

% mkdir objdir
% cd objdir
% srcdir/configure [options] [target]
../gcc/configure --prefix=/home/malabdali/gcc-12 --enable-languages=c,c++ --disable-multilib

if it succeded makefiles it will be generated here (objdir)
not that build results will be saved here /home/malabdali/gcc-12

5- start call make

sudo make -j 8

6- call make install

sudo make install

after install you can find folder(bin,lib,include) inside /home/malabdali/gcc-12

C++ GMock

add it to cmake

target_link_libraries (target_name gtest_main gmock_main gtest )

Matchers

MATCHER_P(M1,p,""){
return p>10;
}

TEST(TESTS,t1){
    testing::Matcher<int> m1(12);
    EXPECT_THAT(12,m1);
    EXPECT_THAT(true,M1(12));
    EXPECT_TRUE(m1.Matches(12));
    EXPECT_THAT(12,testing::AllOf(testing::Gt(0),testing::Lt(100)));
}

expect call

class A{
public:
    virtual void fun()const=0;
    virtual void fun2(int l)const=0;
};
class MockA:public A{
public:
    MOCK_METHOD(void,fun,(),(const override));
    MOCK_METHOD(void,fun2,(int),(const override));
};

using ::testing::AtLeast;                         // #1

TEST(PainterTest, CanDrawSomething) {
    MockA turtle;
    EXPECT_CALL(turtle, fun2(22))                  // #3
            .Times(2);//expect call this function twice with argument 22
    turtle.fun2(22);// #2//first call
    turtle.fun2(22);// #2// second call 
    // test passed because this function called twice
}

C++ GTest

install GTest with CMake

https://google.github.io/googletest/quickstart-cmake.html

include(FetchContent)
FetchContent_Declare(
        googletest
        URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
enable_testing()

target_link_libraries(
        target_name
        gtest_main gtest
)

first example

#include <gtest/gtest.h>
#include "random"

TEST(test1,t1){
    std::default_random_engine re(std::chrono::system_clock::now().time_since_epoch().count());
    std::uniform_int_distribution<uint8_t>rn(0,10);
    int v1=rn(re),v2=rn(re);
    EXPECT_EQ(v1,v2) << "Vectors x and y are of unequal length";
}
int main(int argc, char *argv[]) {
    testing::InitGoogleTest(&argc,argv);
    return RUN_ALL_TESTS();
}
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from test1
[ RUN      ] test1.t1
[       OK ] test1.t1 (0 ms)
[----------] 1 test from test1 (0 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[  PASSED  ] 1 test.

EXPECT AND ASSERT

  • ASSERT: Fails fast, aborting the current function.
  • EXPECT: Continues after the failure.

expect throw and death

Test Fixture

class MyTest : public ::testing::Test {

protected:
    void SetUp()override { //done before test like MyTest constructor
        val=12;
    }
    void TearDown()override{//done after test like ~MyTest

    }
    void add(){
        val+=12;
    }
    int val;
};

TEST_F(MyTest, t1) {
    add();
    EXPECT_EQ(val,66)<<string(__FILE__)+":"+string(std::to_string(__LINE__));
}

Disabling and Skipping Tests

Test Filtering

you can run it with parameters to add more options for testing
for filtering, you can add

--gtest_filter=*.f*

Parametrized Test

class LeapYearParameterizedTestFixture :public ::testing::TestWithParam<int> {
protected:
    int val;
};

TEST_P(LeapYearParameterizedTestFixture, OddYearsAreNotLeapYears) {
    val = GetParam();
    ASSERT_EQ(val,12);
}

INSTANTIATE_TEST_CASE_P(
        LeapYearTests,
        LeapYearParameterizedTestFixture,
        ::testing::Values(
                1, 711, 12, 2013
        ));

Mastering CMake (2)

Setting Up Your First CMake Project

cmake_minimum_required(VERSION 3.20)
project(Hello)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_executable(Hello hello.cpp)

project(<PROJECT-NAME>
        [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
        [DESCRIPTION <project-description-string>]
        [HOMEPAGE_URL <url-string>]
        [LANGUAGES <language-name>...])

cmake_minimum_required(VERSION 3.20.0)
project(Rental CXX)
add_executable(Rental
               main.cpp
               cars/car.cpp  
               # more files in other directories 
)

Working with Targets

Essentially, it’s a recipe that a buildsystem uses to compile a list of files into another file. It can be a .cpp implementation file compiled into an .o object file, a group of .o files packaged into an .a static library, and many other combinations.

All that’s required is an add_executable() command with the name of the executable target and a list of the files that are to be its elements

In CMake, we can create a target using one of three commands:

  • add_executable()
  • add_library()
  • add_custom_target()

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [<source>...])

add_executable(<name> [WIN32] [MACOSX_BUNDLE]
               [EXCLUDE_FROM_ALL]
               [source1] [source2 ...])

for build you can specify a target or build all project

--build binary_directroy --target [all|target]

add source to target

add_executable(main main.cpp)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
  target_sources(main PRIVATE gui_linux.cpp)
elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows")
  target_sources(main PRIVATE gui_windows.cpp)
endif()

preprocessing definitions

add paths to include

target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
  <INTERFACE|PUBLIC|PRIVATE> [item1...]
  [<INTERFACE|PUBLIC|PRIVATE> [item2...] ...])

add definition

set(VAR 8)
add_executable(defined definitions.cpp)
target_compile_definitions(defined PRIVATE ABC
  "DEF=${VAR}")
int main() {
#if defined(ABC)
    std::cout << "ABC is defined!" << std::endl;
#endif
#if (DEF < 2*4-3)
    std::cout << "DEF is greater than 5!" << std::endl;
#endif
}

compiler flags

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -DNO_DEBUG")
set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")

Managing Dependencies with CMake

  • A static library has a .a extension on Unix-like systems and .lib on Windows.
  • Shared libraries have a .so extension on Unix-like systems and .dll on Windows.

if you install library like

apt install protobuf-compiler libprotobuf-dev

find_package(Protobuf REQUIRED)
target_link_libraries(main PRIVATE ${Protobuf_LIBRARIES})
target_include_directories(main PRIVATE 
  ${Protobuf_INCLUDE_DIRS}
  ${CMAKE_CURRENT_BINARY_DIR})

find_package(Protobuf REQUIRED) asks CMake to run the bundled FindProtobuf.cmake find-module and set up the Protobuf library for us. That find-module will scan commonly used paths and (because we provided the REQUIRED keyword) terminate if a library is not found. It will also specify useful variables and functions (such as the one on the next line).
target_link_libraries adds libraries (static or shared) found by find_package() to the linking command of our main target.

  • <PKG_NAME>_FOUND
  • <PKG_NAME>_INCLUDE_DIRS or <PKG_NAME>_INCLUDES
  • <PKG_NAME>_LIBRARIES or <PKG_NAME>_LIBRARIES or <PKG_NAME>_LIBS
  • <PKG_NAME>_DEFINITIONS
  • IMPORTED targets specified by the find-module or config-file

find_package(<Name> [version] [EXACT] [QUIET] [REQUIRED])

for cmake files have format <package>Config.cmake

find_package(Foo CONFIG REQUIRED)
target_link_libraries(boo foo)
add_library(MyLib SHARED ./lib/MyLib.cpp)
target_link_directories(MyLib PUBLIC ./lib)
target_include_directories(MyLib PUBLIC ./lib)
find_package(TBB REQUIRED)
target_link_libraries(MyLib PUBLIC tbb)

#libraries linked to MyLib target like tbb will used inside test target because it linked with MyLib and MyLib is public link with tbb

add_executable(test main.cpp ${res_files})
find_package(Qt6 REQUIRED COMPONENTS Core)
target_link_libraries(test PRIVATE Qt6::Core)
target_link_libraries(test  PUBLIC MyLib)