Montag, 29. Februar 2016

14. März: Test-Driven-Development mit C++

Als Vorbereitung für die kommenden Coding Dojos werden wir dieses Mal eine etwas tiefere Einführung in Test-Driven-Development (TDD) geben und Beispiele und Übungen mit Googletest und Googlemock machen.

Agenda

  1. Einführung in Unittests, TDD und Test Doubles
  2. Übungen (Coding Dojo) wenn wir noch Zeit haben
  3. Verlosung von Sponsoren Goodies
  4. Apéro

Ort

bbv Software Services AG, Blumenrain 10, Luzern, 1. Stock

Datum

Mo 14.3.2016, 19 - 21 Uhr (Anschliessend Apero)

Mitbringen

  • Notebook mit Browser
  • Spass, Mut und Enthusiasmus

Es sind alle herzlich Willkommen die gerne mit C++ programmieren. Egal ob Anfänger oder Experte.

Wir freuen uns auf dich.

Anmeldung

Erfolgt über die Veranstaltung unserer Xing Gruppe C++ Usergroup Zentralschweiz, über  den Meetup Event oder über das Kontaktformular rechts. 

Wir freuen uns auf dich.

Montag, 22. Februar 2016

C++ Idioms

Am letzten Meeting haben wir uns ein paar C++ Idiome angeschaut. Hier eine Zusammenfassung der behandelten Idiome.
Die Lösungen zu den Übungen findet ihr sowohl unten in den Beispielen als auch unter  github.com/meshell/Cpp-Idioms.

C++ Idioms

A programming idiom is a recurring construct in a programming language. It is important to know the idioms associated with a programming language and how to use them for gaining fluency in that language.
Idioms are similar to patterns but usually smaller, programming language specific and do cover algorithms and concepts rather than design issues.

RAII

Intent

  • To guarantee release of resource(s) at the end of a scope
  • To provide basic exception safety guarantee

Description

Resource Acquisition Is Initialization (RAII), is a C++ programming technique which binds the life cycle of a resource (allocated memory, open socket, open file, locked mutex, database connection—anything that exists in limited supply) to the lifetime of an object with automatic storage duration. RAII guarantees that the resource is available to any function that may access the object (resource availability is a class invariant). It also guarantees that all resources are released when their controlling objects go out of scope, in reverse order of acquisition. Likewise, if resource acquisition fails (the constructor exits with an exception), all resources acquired by every fully-constructed member and base subobject are released in reverse order of initialization. This leverages the core language features (object lifetime, scope exit, order of initialization and stack unwinding) to eliminate resource leaks and guarantee exception safety. Another name for this technique is Scope-Bound Resource Management (SBRM).

Example

#include <cstdio>
#include <cstdlib>
#include <memory>
int main()
{
auto file_open = [](const char* filename, const char* mode) -> FILE*
{
return std::fopen(filename, mode);
};
auto file_deleter=[](FILE* file) {
std::puts("Close file\n");
std::fclose(file);
};
// Using a unique pointer and custom deleter (lamda)
std::unique_ptr<FILE, decltype(file_deleter)> fp{file_open("test.txt", "r"),
file_deleter};
if(!fp) {
std::perror("File opening failed");
return EXIT_FAILURE;
}
int c{};
while ((c = std::fgetc(fp.get())) != EOF) {
std::putchar(c);
}
if (std::ferror(fp.get())) {
std::puts("I/O error when reading");
return EXIT_FAILURE;
} else if (std::feof(fp.get())) {
std::puts("End of file reached successfully");
}
}

References

Smart pointer

Intent

  • Avoid manual memory management to improve safety and reduce bugs and memory leaks.
  • Declare ownership explicitly

Description

// unique object ownership
auto unique = std::unique_ptr<widget>(new widget());
auto unique = std::make_unique<widget>();
// shared object ownership
auto shared = std::make_shared<widget>();
// weak reference to an object managed by std::shared_ptr
std::weak_ptr<widget> weak = shared;
auto shared_tmp = weak.lock();

Example

#ifndef DOUBLE_LINKED_LIST_H
#define DOUBLE_LINKED_LIST_H
#include <vector>
#include <memory>
#include <iostream>
template<typename T>
struct Node {
explicit Node(T val)
: value{val}
{}
~Node() {
std::cout << "Node " << value << " destroyed\n";
}
Node(const Node&) = default;
Node(Node&&) = default;
Node& operator=(const Node&) = default;
Node& operator=(Node&&) = default;
T value;
std::shared_ptr<Node> next = nullptr;
std::weak_ptr<Node> previous; // using a shared_ptr would introduce circular dependencies
};
template<typename T>
class DoubleLinkedList {
public:
void push_front (T x);
void push_back (T x);
std::vector<T> get_nodes_forward() ;
std::vector<T> get_nodes_reverse ();
private:
std::shared_ptr<Node<T>> front = nullptr;
std::shared_ptr<Node<T>> back = nullptr;
};
template<typename T>
void DoubleLinkedList<T>::push_front(T x)
{
const auto n = std::make_shared<Node<T>>(x);
if( not front) {
front = n;
back = n;
} else {
front->previous = n;
n->next = front;
front = n;
}
}
template<typename T>
void DoubleLinkedList<T>::push_back(T x)
{
const auto n = std::make_shared<Node<T>>(x);
if( not back) {
front = n;
back = n;
} else {
back->next = n;
n->previous = back;
back = n;
}
}
template<typename T>
std::vector<T> DoubleLinkedList<T>::get_nodes_forward()
{
auto temp = front;
std::vector<T> out;
while(temp) {
out.push_back(temp->value);
temp = temp->next;
}
return out;
}
template<typename T>
std::vector<T> DoubleLinkedList<T>::get_nodes_reverse()
{
auto temp = back;
std::vector<T> out;
while(temp) {
out.push_back(temp->value);
temp = temp->previous.lock();
}
return out;
}
#endif

References

PIMPL

Intent

  • Remove compilation dependencies on internal class implementations and improve compile times.

Description

When anything in a header file class definition changes, all users of that class must be recompiled – even if the only change was to the private class members that the users of the class cannot even access. The PIMPL idiom hides private members from any users of the header file, allowing these internal details to change without requiring recompilation of the client code.

Example

#include "pimpl.h"
#include <iostream>
#include <string>
#include <vector>
class foo::impl {
public:
void initialize() {
member1 = "Hello";
member2 = "World";
member3 = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
}
void print() {
print_member3();
std::cout << member1 << member2 << std::endl;
}
void print_member3() {
auto it = std::begin(member3);
do {
std::cout << (*it);
if(++it != std::end(member3)) {
std::cout << ", ";
}
} while (it != std::end(member3));
std::cout << "..." << std::endl;
}
private:
std::string member1;
std::string member2;
std::vector<int> member3;
};
foo::foo(): pimpl(std::make_unique<impl>()) {
pimpl->initialize();
}
foo::~foo() = default;
foo::foo(const foo& rhs) : pimpl(std::make_unique<impl>(*rhs.pimpl))
{}
foo& foo::operator=(const foo& rhs) {
*pimpl = *rhs.pimpl;
return *this;
}
foo::foo(foo&&) = default;
foo& foo::operator=(foo&&) = default;
void foo::print() {
pimpl->print();
}
view raw CppUG_pimpl.cpp hosted with ❤ by GitHub
#ifndef PIMPL_H
#define PIMPL_H
#include <memory>
class foo
{
public:
foo();
void print();
~foo();
foo(const foo&);
foo& operator=(const foo&);
foo(foo&&);
foo& operator=(foo&&);
private:
class impl;
std::unique_ptr<impl> pimpl;
};
#endif
view raw CppUG_pimpl.h hosted with ❤ by GitHub

References

Rule of Five

Intent

  • Safely and efficiently implement RAII to encapsulate the management of dynamically allocated resources.

Description

The rule of five is a modern expansion of the rule of three. Firstly, the rule of three specifies that if a class implements any of the following functions, it should implement all of them:
  • copy constructor 
  • copy assignment operator 
  • destructor 
These functions are usually required only when a class is manually managing a dynamically allocated resource, and so all of them must be implemented to manage the resource safely. In addition, the rule of five identifies that it usually appropriate to also provide the following functions to allow for optimized copies from temporary objects:
  • move constructor 
  • move assignment operator

Example

#include <cstring>
#include <iostream>
class foo
{
public:
// Constructor
explicit foo(const char* arg) : cstring{new char[std::strlen(arg)+1]}
{
std::strcpy(cstring, arg);
std::cout << "constructed\n";
}
// Destructor
~foo() {
delete[] cstring;
std::cout << "destructed\n";
}
// Copy constructor
foo(const foo& other) : cstring{new char[std::strlen(other.cstring) + 1]}
{
std::strcpy(cstring, other.cstring);
std::cout << "copy constructed\n";
}
// Move constructor
foo(foo&& other) noexcept : cstring{std::move(other.cstring)}
{
other.cstring = nullptr;
std::cout << "move constructed\n";
}
// Copy assignment
foo& operator=(const foo& other) {
foo tmp{other}; // re-use copy-constructor
*this = std::move(tmp); // re-use move-assignment
std::cout << "copy assigned\n";
return *this;
}
// move assignment
foo& operator=(foo&& other) noexcept {
delete[] cstring;
cstring = std::move(other.cstring);
other.cstring = nullptr;
std::cout << "move assigned\n";
return *this;
}
private:
char* cstring; // raw pointer used as a handle to a dynamically-allocated memory block
};

References

Copy & Swap

Intent

  • To create an exception safe implementation of overloaded assignment operator.

Description

Copy assignment and move assignment operators can be expressed in terms of move constructor, destructor, and the swap() member function, if one is provided. For the move assignment operator this comes at the cost of one additional call to the move constructor , which is often acceptable.

Example

#include <cstring>
#include <iostream>
class foo
{
public:
explicit foo(const char* arg) : cstring{new char[std::strlen(arg)+1]} {
std::strcpy(cstring, arg);
}
~foo() {
delete[] cstring;
}
foo(const foo& other) : cstring{new char[std::strlen(other.cstring) + 1]} {
std::strcpy(cstring, other.cstring);
}
foo(foo&& other) noexcept : cstring{other.cstring} {
other.cstring = nullptr;
}
// Copy assignment and Move assignment
foo& operator=(foo other) // pass by value
{
std::swap(cstring, other.cstring);
return *this;
}
private:
char* cstring;
};

References

Rule of Zero

Intent

  • Utilise the value semantics of existing types to avoid having to implement custom copy and move operations.

Description

Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership and support the appropriate copy/move semantics. Other classes therefore should not have custom destructors, copy/move constructors or copy/move assignment operators.

Example

#include <string>
class foo {
public:
// Constructor
foo(const std::string& arg) : cppstring(arg) {}
private:
std::string cppstring
};
When a base class is intended for polymorphic use, its destructor may have to be declared public and virtual. This blocks implicit moves (and deprecates implicit copies), and so the special member functions have to be declared as defaulted.
class base {
public:
virtual ~base() = default;
base(const base&) = default;
base(base&&) = default;
base& operator=(const base&) = default;
base& operator=(base&&) = default;
};

References

Erase-Remove

Intent

  • Eliminate elements from a STL container.

Example

#include <vector>
#include <algorithm>
int main() {
std::vector<int> v = {1, 2, 3, 4, 2, 5, 2, 6};
v.erase(std::remove(std::begin(v),
std::end(v),
2),
std::end(v));
// using a custom predicate
v.erase(std::remove_if(std::begin(v),
std::end(v),
[](int i) {
return i%2 == 0;
}),
std::end(v));
}

References

Type Generator

Intent

  • Simplify creation of complex template-based types
  • Synthesize a new type or types based on template argument(s)
  • Localize default policies when policy-based class design is used

Example

// Until C++11
template<class T>
struct p
{
typedef T* Type;
};
p<float>::Type y; // y is of type float*
// Since C++11: alias template
template<class T>
using ptr = T*;
ptr<int> x; // x is of type int*

References

Overriding Virtual Functions

Intent

  • Override a virtual function of a base class in a safe manner.

Description

Overriding virtual functions may cause problems, for example during refactoring, when renaming a (non pure) virtual base method. Because the compiler cannot warn you that you forgot to replace the overridden methods in the specialized classes you may actually declare a new method in the specialized class instead of overriding one. Therefore as a guideline when using C++11 or higher is: Always write override when you intend to override a virtual function.

Example

struct base {
virtual std::string name() { return "default"; }
};
struct derived : public base {
// compile error: 'std::string derived::mame()' marked 'override', but does not override
std::string mame() override { return "derived!" }
};

References

Prohibit derivation

Intent

  • Prohibit to further override a virtual function.
  • Prohibit a class to have further-derived classes.

Description

Writing final makes a virtual function no longer overrideable in further-derived classes, or a class no longer permitted to have further-derived classes.

Example

struct base {
virtual std::string name() { return "default"; }
virtual void foo() final { }
};
struct derived final: public base {
std::string name() override { return "derived!" }
// compile error: overriding final function 'virtual void base::foo()'
void foo() override { }
};
// compile error: cannot derive from 'final' base 'derived'
struct further_derived : public derived {
std::string name() override { return "further derived!" }
};
view raw CppUG_final.h hosted with ❤ by GitHub

References

Shrink to fit

Intent

  • Minimize the capacity of a container just enough to hold existing range.

Description

Since C++11 the Shrink-to-fit idiom is directly supported.

Example

#include <vector>
// Until C++11
std::vector<int> v;
//v is swapped with its temporary copy, which is capacity optimal
std::vector<int>(v.begin(), v.end()).swap(v);
// Since C++11
std::vector<int> v2;
v2.shrink_to_fit();

References

Range-based for loop

Intent

  • Executes a for loop over a range of values, such as all elements in a container.

Description

Since C++11 the Range-based for loop is used as a more readable equivalent to the traditional for loop.

Example

#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for(const auto& i : v) {
std::cout << i << ' ';
}
std::cout << '\n';
int a[] = {0, 1, 2, 3, 4, 5};
for(const auto& n : a) {
std::cout << n << ' ';
}
std::cout << '\n';
}

References

AAA (Almost always auto)

Intent

  • Specify that the type of the variable that is being declared will be automatically deduced from its initializer.
  • Write code against interfaces, not implementations.
  • Prevent correctness and performance issues that can bedevil manual type declarations.

Description

When declaring variables in block scope, in namespace scope, in init statements of for loops, etc, the keyword auto may be used as the type specifier. Once the type of the initializer has been determined, the compiler determines the type that will replace the keyword auto using the rules for template argument deduction from a function call.

Examples

// Classic C++ declaration order // Modern C++ style
const char* s = "Hello"; auto s = "Hello";
widget w = get_widget(); auto w = get_widget();
employee e{ empid }; auto e = employee{ empid };
widget w{ 12, 34 }; auto w = widget{ 12, 34 };
unique_ptr<widget> w auto w = make_unique<widget>();
= make_unique<widget>();
int x = 42; auto x = 42;
float x = 42.; auto x = 42.f;
unsigned long x = 42; auto x = 42ul;
std::string x = "42"; auto x = "42"s; // C++14
chrono::nanoseconds x{ 42 }; auto x = 42ns; // C++14
view raw CppUG_AAA.cpp hosted with ❤ by GitHub

References

Use Standard Library Algorithms

Intent

  • Use the standard library algorithms rather than reinventing the wheel.

Examples

#include <algorithm>
#if __cplusplus > 201103L
// for C++14 use
using std::cbegin;
using std::cend;
#else
// C++11 does not implement non-member version of cbegin and cend.
// Therefor for a C++11 only compiler use
template<class C>
auto cbegin(const C& container)->decltype(std::begin(container)) {
// invoking the non-member begin function on a const container yields a const-iterator.
return std::begin(container);
}
template<class C>
auto cend(const C& container)->decltype(std::end(container)) {
// invoking the non-member end function on a const container yields a const-iterator.
return std::end(container);
}
#endif
template<typename C>
void sort_ascending(C& container) {
using std::begin;
using std::end;
std::sort(std::begin(container), std::end(container));
}
template<typename C>
void sort_descending(C& container) {
using std::begin;
using std::end;
std::sort(std::begin(container), std::end(container), std::greater<typename C::value_type>());
}
template<class C>
void sort_and_remove_duplicates(C& container) {
using std::begin;
using std::end;
std::sort(begin(container), end(container));
container.erase(std::unique(begin(container), end(container)), end(container));
}
template<class C>
bool has_even_elements(C& container) {
return std::any_of(cbegin(container), cend(container), [](int i){ return i % 2 == 0; }));
}
view raw CppUG_STL.h hosted with ❤ by GitHub

References

constexpr

Intent

  • To have an integral value that is const and known during compilation.
  • To place values in read-only memory.

Example

#include <array>
#include <iostream>
#if __cplusplus > 201103L
constexpr int pow(int base, int exp) noexcept
{
auto result = 1;
for (int i = 0; i < exp; ++ i) {
result *= base;
}
return result;
}
#else
constexpr int pow(int base, int exp) noexcept
{
return (exp == 0 ? 1 : base * pow(base, exp -1));
}
#endif
int main()
{
// We need to store all possible combinations of n values with 3 states
constexpr auto n = 5; // n is 5
std::array<int, pow(3, n)> results;
std::cout << results.size() << '\n';
}

References

  • Item 15 of "Effective Modern C++" by Scott Meyers, O'Reilly, 2014

User-defined literals

Intent

  • Produce objects of user-defined type by defining a user-defined suffix.

Example

#include <iostream>
#include <chrono>
// used as conversion
constexpr long double operator"" _m ( long double m ) {
return m;
}
constexpr long double operator"" _km ( long double km ) {
return km * 1000.0_m;
}
constexpr long double operator"" _nmi ( long double nmi ) {
return nmi * 1852.0_m;
}
// used for side-effects
void operator"" _print ( const char* str ) {
std::cout << str << '\n';
}
int main() {
const double distance_sea = 150.0_nmi;
const double distance = distance_sea / 1.0_km;
std::cout << std::fixed << distance_sea << "m\n";
std::cout << std::fixed << distance << "km\n";
0x123ABC_print;
#if __cplusplus > 201103L
// With C++14 some literal operators are defined in the standard library
using namespace std::chrono_literals;
auto d1 = 250ns;
std::chrono::nanoseconds d2 = 1us;
std::cout << "250ns = " << d1.count() << " nanoseconds\n"
<< "1us = " << d2.count() << " nanoseconds\n";
#endif
}

References

Static Assertion

Intent

  • Perform compile-time assertion checking.

Example

#include <type_traits>
template <class T>
void swap(T& a, T& b) {
static_assert(std::is_copy_constructible<T>::value,
"Swap requires copying");
static_assert(std::is_nothrow_move_constructible<T>::value
&& std::is_nothrow_move_assignable<T>::value,
"Swap may throw");
auto c = b;
b = a;
a = c;
}

References

Placement Insert aka emplace

Intent

  • Creating objects in place while inserting into a container.
  • Enable insertion of elements that are not CopyConstructable.

Example

#include <string>
#include <vector>
struct Type
{
Type(std::string, float) {};
Type(Type const &) = delete;
Type(Type &&) = default;
};
int main()
{
std::vector<Type> values;
values.emplace_back("pi", 3.14);
}

References