Skip to content

Commit

Permalink
cpp17 vs cpp20
Browse files Browse the repository at this point in the history
  • Loading branch information
kris-jusiak committed Oct 30, 2024
1 parent 705de18 commit 32d4ca0
Show file tree
Hide file tree
Showing 17 changed files with 834 additions and 0 deletions.
15 changes: 15 additions & 0 deletions cpp17_vs_cpp20/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# C++17 vs C++20

> https://eel.is/c++draft/
## Core
[initializer](initializer.md) | [lambda](lambda.md) | [concepts](concepts.md) | [constexpr](constexpr.md) | [consteval](consteval.md) | [coroutines](coroutines.md) | [modules](modules.md) | [misc](misc.md)

## STL
[format](format.md) | [ranges](ranges.md)

## Meta
[nttp](nttp.md) | [fixed_string](fixed_string.md) | [reflection](reflection.md) | [tmp](tmp.md)

## Others
[testing](testing.md) | [performance](performance.md)
62 changes: 62 additions & 0 deletions cpp17_vs_cpp20/concepts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Concepts

> syntax
C++17
```cpp
template<class T> auto parse(T);
```
C++20:
```cpp
auto parse(auto);
```

> Note: auto = weakest concept // template<class T> Auto = true; // always satisfied
> constraints
C++17 - SFINAE
```cpp
template<class T, std::enable_if_t<Constrained, bool> = true>
auto parse(T);
```
C++20
```cpp
template<class T> concept = Constrained;
template<Constrained T> auto parse(T);
```
or
```cpp
template<class T> requires Constrained<T> auto parse(T);
```
Note: destructors can be constrained and there is not need for type
```cpp
~parser() requires std::abstract<T> = default;
```

Note: tere syntax
```cpp
auto parse(Constrained auto);
```
> Design by introspection (Andrei Alexandrescu)
C++17
```cpp
template<class T> using has_foo = decltype(std::declval<T&>().foo);
if constexpr(std::is_detected<has_foo, T>) {
// ...
}
```

C++20
```cpp
if constexpr (requires(T t) { t.foo; }) {
// ...
}
```
15 changes: 15 additions & 0 deletions cpp17_vs_cpp20/consteval.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# consteval - like constexpr but only at compile-time

```cpp
[[nodiscard]] consteval parse(auto t) {
// ...
return t;
}
```
```cpp
int main(int argc, char**) {
constexpr auto = parse(42); // okay
auto = parse(argc); // error, not a constant expression
}
```
75 changes: 75 additions & 0 deletions cpp17_vs_cpp20/constexpr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# constexpr

- virtual
- std::unique_ptr
- std::string
- std::vector
- ...

> Example - constexpr std::function
```cpp
template <class>
class function;

template <class R, class... TArgs>
class function<R(TArgs...)> {
struct interface {
constexpr virtual auto operator()(TArgs...) -> R = 0;
constexpr virtual ~interface() = default;
};

template <class Fn>
struct implementation final : interface {
constexpr explicit(true) implementation(Fn fn) : fn{fn} {}
constexpr auto operator()(TArgs... args) -> R { return fn(args...); }

private:
Fn fn{};
};

public:
template <class Fn>
constexpr function(Fn fn) : fn{std::make_unique<implementation<Fn>>(fn)} {}

constexpr auto operator()(TArgs... args) const -> R {
return (*fn)(args...);
}

private:
std::unique_ptr<interface> fn{};
};

template <class>
struct function_traits {};

template <class R, class B, class... TArgs>
struct function_traits<R (B::*)(TArgs...) const> {
using type = R(TArgs...);
};

template <class F>
function(F) -> function<typename function_traits<decltype(&F::operator())>::type>;
```
```cpp
consteval auto test_empty() {
function f = [] { return 42; };
return f();
}
consteval auto test_arg() {
function f = [](int i) { return i; };
return f(42);
}
consteval auto test_capture() {
int i = 42;
function f = [&] { return i; };
return f();
}
static_assert(42 == test_empty());
static_assert(42 == test_arg());
static_assert(42 == test_capture());
```
78 changes: 78 additions & 0 deletions cpp17_vs_cpp20/coroutines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# State Machine

C++17
```cpp
constexpr auto establish = [] { std::puts("establish"); };
constexpr auto close = [] { std::puts("close"); };
constexpr auto is_valid = [] { return true; };
constexpr auto reset = [] { std::puts("reset"); };

sml::sm connection = [] {
using namespace sml;
return transition_table{
* "Disconnected"_s + event<connect> / establish = "Connecting"_s,
"Connecting"_s + event<established> = "Connected"_s,
"Connected"_s + event<ping> [ is_valid ] / reset_timeout,
"Connected"_s + event<timeout> / establish = "Connecting"_s,
"Connected"_s + event<disconnect> / close = "Disconnected"_s
};
};
```
```
int main() {
connection.process_event(connect{});
connection.process_event(established{});
connection.process_event(ping{});
connection.process_event(disconnect{});
}
```
C++20
```cpp
constexpr auto establish = [] { std::puts("establish"); };
constexpr auto close = [] { std::puts("close"); };
constexpr auto is_valid = [] { return true; };
constexpr auto reset = [] { std::puts("reset"); };
class Connection {
state connection() {
for (;;) {
disconnected:
if (auto [event, data] = co_await sm; event == connect{}) {
establish();
connecting:
if (auto [event, data] = co_await sm; event == established{}) {
connected:
switch (auto [event, data] = co_await sm; event) {
case ping{}:
if (is_valid(data)) reset();
goto connected;
case timeout{}:
establish();
goto connecting;
case disconnect{}:
close();
goto disconnected;
}
}
}
}
}
public:
void process_event(const auto& event) { sm.process_event(event); }
private:
state_machine<int> sm{};
state initial{connection()};
};
int main() {
Connection connection{};
connection.process_event(connect{});
connection.process_event(established{});
connection.process_event(ping{});
connection.process_event(disconnect{});
}
```
56 changes: 56 additions & 0 deletions cpp17_vs_cpp20/fixed_string.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# fixed_string (compile-time string)

```cpp
template<char....> struct ct_string{};
```

C++17 via gnu-extension

```cpp
template<class T, T... Cs>
constexpr auto operator""_cs() { return ct_string<Cs...>{}; }

template<class> struct foo {};
foo<decltype("bar"_cs)>;
```
C++20 via fixed_string
```cpp
template <std::size_t N>
struct fixed_string final {
constexpr explicit(true) fixed_string(const auto... cs) : data{cs...} {}
constexpr explicit(false) fixed_string(const char (&str)[N + 1]) {
std::copy_n(str, N + 1, std::data(data));
}
[[nodiscard]] constexpr auto operator<=>(const fixed_string&) const = default;
[[nodiscard]] constexpr explicit(false) operator std::string_view() const {
return {std::data(data), N};
}
[[nodiscard]] constexpr auto size() const -> std::size_t { return N; }
std::array<char, N + 1> data{};
};
```

```cpp
template<fixed_string> struct foo {};
foo<"bar"> f{};
```
> Note: fixed_string doesn't have external linkage
```cpp
template <stdext::fixed_string Str>
static auto to_string = []<auto... Ns>(std::index_sequence<Ns...>) {
return ct_string<Str.data[Ns]...>{};
}(std::make_index_sequence<Str.size()>{});
```

```cpp
static_assert(ct_string<'f', 'o', 'o'> == to_string<"foo">)
```
15 changes: 15 additions & 0 deletions cpp17_vs_cpp20/format.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# std.format

C++17
fmt.format

C++20
```cpp
static_assert("42"sv == fmt::format("{}", 42));
static_assert("42.."sv == fmt::format("{:.<4}", 42));
static_assert("..42"sv == fmt::format("{:.>4}", 42));
static_assert("42 0x2a"sv == fmt::format("{0} {0:#x}", 42)); // use 0 parameter
static_assert(" QL "sv == fmt::format("{:^6}", "QL"));
static_assert(" QL "sv == fmt::format("{:>4} ", "QL"));
static_assert("QL "sv == fmt::format("{:<{}}", "QL", 10));
```
22 changes: 22 additions & 0 deletions cpp17_vs_cpp20/initializer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Designated initliazers

```cpp
struct msg {
int type;
int value;
}
```
C++17
```cpp
auto m = msg{42, 42}; // error prone
```

> for safety strong types would be preferred


C++20
```cpp
auto m = msg{.type = 42, .value = 42};
```
49 changes: 49 additions & 0 deletions cpp17_vs_cpp20/lambda.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Lambdas

```cpp
template<auto N> constexpr auto unroll = [](auto expr) {
[expr]<auto ...Is>(std::index_sequence<Is...>) {
((expr(), void(Is)), ...);
}(std::make_index_sequence<N>{});
};
```

```cpp
int main() {
unroll<2>([]{ std::puts("!"); });
}
```

```asm
.LC0: .string "!"
main:
sub rsp, 8
mov edi, OFFSET FLAT:.LC0
call puts
mov edi, OFFSET FLAT:.LC0
call puts
xor eax, eax
add rsp, 8
ret
```

Note: How to call templated lambda?

```cpp
auto l = []<auto...> {};
l.template operator()<42>()
```

> Lambda types
```cpp
static_assert(typeid([]{}) != typeid([]{}));
```
```cpp
static_assert(+[]{} != +[]{});
```

```cpp
auto expr = decltype([]{}){};
```
Loading

0 comments on commit 32d4ca0

Please sign in to comment.