mirror of
https://github.com/cirosantilli/linux-kernel-module-cheat.git
synced 2026-01-23 02:05:57 +01:00
learn more c++, it never ends
This commit is contained in:
22
README.adoc
22
README.adoc
@@ -15496,7 +15496,7 @@ Programs under link:userland/cpp/[] are examples of https://en.wikipedia.org/wik
|
|||||||
* templates
|
* templates
|
||||||
** link:userland/cpp/template.cpp[]: basic example
|
** link:userland/cpp/template.cpp[]: basic example
|
||||||
** link:userland/cpp/template_class_with_static_member.cpp[]: https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template
|
** link:userland/cpp/template_class_with_static_member.cpp[]: https://stackoverflow.com/questions/3229883/static-member-initialization-in-a-class-template
|
||||||
** link:userland/cpp/if_constexpr.cpp[]: C++17 `if constexpr`
|
** link:userland/cpp/if_constexpr.cpp[]: C++17 `if constexpr`: https://stackoverflow.com/questions/12160765/if-else-at-compile-time-in-c/54647315#54647315
|
||||||
* iostream
|
* iostream
|
||||||
** link:userland/cpp/copyfmt.cpp[]: `std::copyfmt` restores stream state, see also: https://stackoverflow.com/questions/12560291/set-back-default-floating-point-print-precision-in-c/53673686#53673686
|
** link:userland/cpp/copyfmt.cpp[]: `std::copyfmt` restores stream state, see also: https://stackoverflow.com/questions/12560291/set-back-default-floating-point-print-precision-in-c/53673686#53673686
|
||||||
* fstream
|
* fstream
|
||||||
@@ -15509,6 +15509,26 @@ Programs under link:userland/cpp/[] are examples of https://en.wikipedia.org/wik
|
|||||||
*** <<algorithms>> contains a benchmark comparison of different c++ containers
|
*** <<algorithms>> contains a benchmark comparison of different c++ containers
|
||||||
*** link:userland/cpp/set.cpp[]: `std::set` contains unique keys
|
*** link:userland/cpp/set.cpp[]: `std::set` contains unique keys
|
||||||
|
|
||||||
|
[[cpp-initialization-types]]
|
||||||
|
==== C++ initialization types
|
||||||
|
|
||||||
|
OMG this is hell, understand when primitive variables are initialized or not:
|
||||||
|
|
||||||
|
* https://stackoverflow.com/questions/3127454/how-do-c-class-members-get-initialized-if-i-dont-do-it-explicitly
|
||||||
|
* https://blog.tartanllama.xyz/initialization-is-bonkers/
|
||||||
|
|
||||||
|
Intuition:
|
||||||
|
|
||||||
|
* direct initialization: a constructor called explicitly with at least one argument: https://en.cppreference.com/w/cpp/language/direct_initialization
|
||||||
|
* default initialization: does not initialize primitive types: https://en.cppreference.com/w/cpp/language/default_initialization
|
||||||
|
* value initialization: maybe initializes primitive types: https://en.cppreference.com/w/cpp/language/value_initialization
|
||||||
|
* zero initialization: initializes primitive types
|
||||||
|
|
||||||
|
Good rule:
|
||||||
|
|
||||||
|
* initialize every single variable explicitly to prevent the risk of having uninitialized variables due to programmer error (which is easy to get wrong due to insane rules)
|
||||||
|
* if you don't define your own default constructor, always `= delete` it instead. This prevents the possibility that variables will be assigned twice due to zero initialization
|
||||||
|
|
||||||
[[cpp-multithreading]]
|
[[cpp-multithreading]]
|
||||||
==== C++ multithreading
|
==== C++ multithreading
|
||||||
|
|
||||||
|
|||||||
@@ -670,6 +670,8 @@ path_properties_tuples = (
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
'count.cpp': {'more_than_1s': True},
|
'count.cpp': {'more_than_1s': True},
|
||||||
|
'initialization_types.cpp': {'cc_flags':
|
||||||
|
['-Wno-unused-variable', LF, '-Wno-unused-but-set-variable', LF]},
|
||||||
'm5ops.cpp': {'allowed_emulators': {'gem5'}},
|
'm5ops.cpp': {'allowed_emulators': {'gem5'}},
|
||||||
'parallel_sort.cpp': {'minimum_gcc_version': (9, 0, 0)},
|
'parallel_sort.cpp': {'minimum_gcc_version': (9, 0, 0)},
|
||||||
'sleep_for.cpp': {
|
'sleep_for.cpp': {
|
||||||
|
|||||||
22
userland/cpp/constexpr_func_infinite_loop.cpp
Normal file
22
userland/cpp/constexpr_func_infinite_loop.cpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// https://cirosantilli.com/linux-kernel-module-cheat#cpp
|
||||||
|
|
||||||
|
constexpr int f() {
|
||||||
|
int i = 0;
|
||||||
|
while (1)
|
||||||
|
i += 1;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int g() {
|
||||||
|
return g();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
#if 0
|
||||||
|
// GCC 9.2.1. error: ‘constexpr’ loop iteration count exceeds limit of 262144 (use ‘-fconstexpr-loop-limit=’ to increase the limit)
|
||||||
|
static_assert(f() == 0);
|
||||||
|
// GCC 9.2.1. error: ‘constexpr’ evaluation depth exceeds maximum of 512 (use ‘-fconstexpr-depth=’ to increase the maximum)
|
||||||
|
static_assert(g() == 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -1,23 +1,30 @@
|
|||||||
// https://cirosantilli.com/linux-kernel-module-cheat#cpp
|
// https://cirosantilli.com/linux-kernel-module-cheat#cpp
|
||||||
|
|
||||||
|
|
||||||
#if __cplusplus >= 201703L
|
#if __cplusplus >= 201703L
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
template <class T>
|
template<typename T>
|
||||||
struct MyClass {
|
struct MyClass {
|
||||||
int myFunc() {
|
MyClass() : myVar{0} {}
|
||||||
if constexpr(std::is_integral<T>())
|
void modifyIfNotConst() {
|
||||||
return 1;
|
if constexpr(!isconst) {
|
||||||
else
|
myVar = 1;
|
||||||
return 2;
|
}
|
||||||
}
|
}
|
||||||
|
T myVar;
|
||||||
|
static constexpr bool isconst = std::is_const<T>::value;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
#if __cplusplus >= 201703L
|
#if __cplusplus >= 201703L
|
||||||
assert(MyClass<int>().myFunc() == 1);
|
MyClass<double> x;
|
||||||
assert(MyClass<float>().myFunc() == 2);
|
MyClass<const double> y;
|
||||||
|
x.modifyIfNotConst();
|
||||||
|
y.modifyIfNotConst();
|
||||||
|
assert(x.myVar == 1);
|
||||||
|
assert(y.myVar == 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
199
userland/cpp/initialization_types.cpp
Normal file
199
userland/cpp/initialization_types.cpp
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
// https://cirosantilli.com/linux-kernel-module-cheat#cpp-initialization-types
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
// zero-initialization because has static storage
|
||||||
|
// just like a static "local" function variable.
|
||||||
|
int global;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
|
||||||
|
// First let's list how different initializations are done explicitly in the code.
|
||||||
|
{
|
||||||
|
struct C {
|
||||||
|
int i;
|
||||||
|
constexpr C() : i(0) {}
|
||||||
|
constexpr C(int i) : i(i) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default-initialization.
|
||||||
|
{
|
||||||
|
C i;
|
||||||
|
C *j = new C;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value initialization.
|
||||||
|
{
|
||||||
|
C i{};
|
||||||
|
C j = C();
|
||||||
|
C k = C{};
|
||||||
|
C *l = new C();
|
||||||
|
C *m = new C{};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Direct initialization.
|
||||||
|
{
|
||||||
|
C i(1);
|
||||||
|
C j{1};
|
||||||
|
C *k = new C(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero initialization
|
||||||
|
{
|
||||||
|
static C i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most vexing parse, function declaration!
|
||||||
|
{
|
||||||
|
C myfunc();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The syntax goes for primitive types. These serve as the basis
|
||||||
|
// for the recursive definition..
|
||||||
|
{
|
||||||
|
// Default.
|
||||||
|
int i;
|
||||||
|
// Following the cases at:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/default_initialization
|
||||||
|
// i is POD and is not an array: therefore nothing is done.
|
||||||
|
//assert(i == ?);
|
||||||
|
|
||||||
|
// Value.
|
||||||
|
constexpr int j{};
|
||||||
|
// Following the cases at:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/value_initialization
|
||||||
|
// j is not a class type, and is not an array type. Therefore it is zero initialized.
|
||||||
|
static_assert(j == 0);
|
||||||
|
|
||||||
|
// Direct.
|
||||||
|
constexpr int k{1};
|
||||||
|
// Following the cases at:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/direct_initialization
|
||||||
|
// is not array, not class, not "if T is a non-class type but the source type is a
|
||||||
|
// class type" whatever that means, and not bool, therefore standard conversion is used
|
||||||
|
// and the value is set.
|
||||||
|
static_assert(k == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, let's see which implicit initializations are done for each case recursively.
|
||||||
|
{
|
||||||
|
{
|
||||||
|
struct C { int i; };
|
||||||
|
static_assert(std::is_default_constructible<C>());
|
||||||
|
static_assert(std::is_aggregate<C>());
|
||||||
|
|
||||||
|
// Default
|
||||||
|
C a;
|
||||||
|
// Following the cases at:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/default_initialization
|
||||||
|
// - non-POD? no
|
||||||
|
// - array? no
|
||||||
|
// - then: nothing is done.
|
||||||
|
//assert(i == ?);
|
||||||
|
|
||||||
|
// Value
|
||||||
|
constexpr C b{};
|
||||||
|
// Following the cases at:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/value_initialization
|
||||||
|
// - class type with no default constructor? no
|
||||||
|
// - default constructor that is neither user-provided nor deleted: yes
|
||||||
|
// - zero initialize
|
||||||
|
// - has a non-trivial default constructor
|
||||||
|
// - no
|
||||||
|
assert(b.i == 0);
|
||||||
|
|
||||||
|
// Aggregate
|
||||||
|
constexpr C c{5};
|
||||||
|
static_assert(c.i == 5);
|
||||||
|
|
||||||
|
// Zero
|
||||||
|
static C d;
|
||||||
|
assert(d.i == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
struct C {
|
||||||
|
int i;
|
||||||
|
constexpr C() : i(3) {};
|
||||||
|
constexpr C(int i) : i(i) {};
|
||||||
|
};
|
||||||
|
static_assert(!std::is_pod<C>());
|
||||||
|
|
||||||
|
// Default
|
||||||
|
constexpr C a;
|
||||||
|
// Following the cases at:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/default_initialization
|
||||||
|
// - non-POD? yes. Therefore: call C(). which initializes i.
|
||||||
|
static_assert(a.i == 3);
|
||||||
|
|
||||||
|
// Value
|
||||||
|
constexpr C b{};
|
||||||
|
// Following the cases at:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/value_initialization
|
||||||
|
// - class type with no default constructor? no
|
||||||
|
// - user-provided default constructor? yes. Therefore, default initialize the object.
|
||||||
|
// So we fall on the above case, and the variable does get set.
|
||||||
|
static_assert(b.i == 3);
|
||||||
|
|
||||||
|
// Direct
|
||||||
|
constexpr C c{5};
|
||||||
|
// Following the cases at:
|
||||||
|
// https://en.cppreference.com/w/cpp/language/_initialization
|
||||||
|
// - array type? no
|
||||||
|
// - class type? yes. Call constructor.
|
||||||
|
static_assert(c.i == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
struct C {
|
||||||
|
int i;
|
||||||
|
constexpr C(int i) : i(i) {};
|
||||||
|
};
|
||||||
|
static_assert(!std::is_pod<C>());
|
||||||
|
static_assert(!std::is_default_constructible<C>());
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Cannot be default initialized if not default constructible.
|
||||||
|
// error: no matching function
|
||||||
|
constexpr C a;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Cannot be value initialized if not default constructible.
|
||||||
|
// error: no matching function
|
||||||
|
C b{};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Direct initialize
|
||||||
|
constexpr C c{5};
|
||||||
|
static_assert(!std::is_pod<C>());
|
||||||
|
// - class type with no default constructor? yes
|
||||||
|
}
|
||||||
|
|
||||||
|
//struct A { T t; A() : t() {} };
|
||||||
|
//A a; // t is value-initialized
|
||||||
|
//A a{}; // t is value-initialized
|
||||||
|
|
||||||
|
//struct A { T t; A() : t{} {} };
|
||||||
|
//A a; // t is TODO
|
||||||
|
//A a{}; //
|
||||||
|
|
||||||
|
//struct A { T t; A() {} };
|
||||||
|
//A a; // t is TODO
|
||||||
|
//A a{}; // t is TODO
|
||||||
|
|
||||||
|
//struct A { T t; A() = default };
|
||||||
|
//A d; // t is TOAO
|
||||||
|
//A d{}; // t is TOAO
|
||||||
|
|
||||||
|
//struct A { T t; A() = deleted };
|
||||||
|
//A e; // t is TODO
|
||||||
|
//A e{}; // t is TODO
|
||||||
|
|
||||||
|
//struct A { T t; A(T t) {} };
|
||||||
|
//A f; // t is TODO
|
||||||
|
//A f{}; // t is TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,18 @@
|
|||||||
// https://cirosantilli.com/linux-kernel-module-cheat#cpp
|
// https://cirosantilli.com/linux-kernel-module-cheat#cpp
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
||||||
struct D {
|
struct D {
|
||||||
int i;
|
int i;
|
||||||
D() {}
|
constexpr D() : i(0) {}
|
||||||
D(int i) : i(i) {}
|
constexpr D(int i) : i(i) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct C {
|
struct C {
|
||||||
int i;
|
int i;
|
||||||
C() : i(1) {}
|
constexpr C() : i(1) {}
|
||||||
C(int i) : i(i) {}
|
constexpr C(int i) : i(i) {}
|
||||||
C(const D& d) : i(d.i) {}
|
constexpr C(const D& d) : i(d.i) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Declares *FUNCTION* called `c` that returns `C` inside function main.
|
// Declares *FUNCTION* called `c` that returns `C` inside function main.
|
||||||
@@ -25,7 +23,7 @@ int main() {
|
|||||||
// Therefore there would be not way for C++ to distinguish between the two,
|
// Therefore there would be not way for C++ to distinguish between the two,
|
||||||
// and still be backwards compatible with C.
|
// and still be backwards compatible with C.
|
||||||
{
|
{
|
||||||
C c();
|
constexpr C c();
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// ERROR: function definition is not possible inside another function.
|
// ERROR: function definition is not possible inside another function.
|
||||||
@@ -37,24 +35,24 @@ int main() {
|
|||||||
|
|
||||||
// If you want to call a default constructor, use:
|
// If you want to call a default constructor, use:
|
||||||
{
|
{
|
||||||
C c;
|
constexpr C c;
|
||||||
assert(c.i == 1);
|
static_assert(c.i == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For non-default constructors, literal arguments disambiguate
|
// For non-default constructors, literal arguments disambiguate
|
||||||
// things as this syntax could not possibly be a function declaration.
|
// things as this syntax could not possibly be a function declaration.
|
||||||
{
|
{
|
||||||
C c(2);
|
constexpr C c(2);
|
||||||
assert(c.i == 2);
|
static_assert(c.i == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// But sometimes even arguments are not enough: here D()
|
// But sometimes even arguments are not enough: here D()
|
||||||
// is interpreted as "a function of type `D f()`"
|
// is interpreted as "a function of type `D f()`"
|
||||||
{
|
{
|
||||||
C c(D(2));
|
constexpr C c(D());
|
||||||
#if 0
|
#if 0
|
||||||
// error: request for member ‘i’ in ‘c’, which is of non-class type ‘main()::C(main()::D (*)())’
|
// error: request for member ‘i’ in ‘c’, which is of non-class type ‘main()::C(main()::D (*)())’
|
||||||
assert(c.i == 2);
|
static_assert(c.i == 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,22 +61,22 @@ int main() {
|
|||||||
{
|
{
|
||||||
// Extra parenthesis.
|
// Extra parenthesis.
|
||||||
{
|
{
|
||||||
C c((D(2)));
|
constexpr C c((D(2)));
|
||||||
assert(c.i == 2);
|
static_assert(c.i == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize through assignment. TODO likely guaranteed to be cost-free,
|
// Initialize through assignment. TODO likely guaranteed to be cost-free,
|
||||||
// but confirm.
|
// but confirm.
|
||||||
{
|
{
|
||||||
C c = C((D(2)));
|
constexpr C c = C((D(2)));
|
||||||
assert(c.i == 2);
|
static_assert(c.i == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializer list. Only works if there is no initializer_list constructor.
|
// Initializer list. Only works if there is no initializer_list constructor.
|
||||||
// Only works in general if c does not have an ambiguous initializer_list constructor though.
|
// Only works in general if c does not have an ambiguous initializer_list constructor though.
|
||||||
{
|
{
|
||||||
C c{D(2)};
|
constexpr C c{D(2)};
|
||||||
assert(c.i == 2);
|
static_assert(c.i == 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user