Last active
May 19, 2023 03:02
-
-
Save vladiant/c740b52282833b5369a070e0970a7b3b to your computer and use it in GitHub Desktop.
Surprising C++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <vector> | |
template <class T, class U, class V> | |
struct Foo { | |
T bar; | |
std::vector<U> baz; | |
V foobar; | |
}; | |
template <class T, class U, class V> | |
Foo(T, std::initializer_list<U>, V) -> Foo<T, U, V>; | |
int main() { | |
// Type of test is Foo<int, double, char> | |
auto test = Foo{42, {3.14, 2.718, 1.618}, 'x'}; | |
std::cout << test.baz[1] << std::endl; | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <utility> | |
template <size_t I> | |
void apply_to_index(auto f, auto... args) { | |
[&]<size_t... Idxs>(std::index_sequence<Idxs...>) { | |
( | |
[&] { | |
if constexpr (Idxs == I) { | |
f(args); | |
} | |
}(), | |
...); | |
} | |
(std::make_index_sequence<sizeof...(args)>{}); | |
} | |
int main() { | |
apply_to_index<2>([](std::string arg) { std::cout << arg << '\n'; }, | |
"hello", 42, "world", 73); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cassert> | |
#include <ranges> | |
#include <string_view> | |
constexpr unsigned my_atoi(std::string_view str) { | |
unsigned rv = 0; | |
for (auto c : str) { | |
// https://www.youtube.com/watch?v=EwEppzQe5Oc claims OK | |
// today 5 Oct 2022 it is not | |
assert(c >= '0' && c <= '9'); | |
rv *= 10; | |
rv += c - '0'; | |
} | |
return rv; | |
} | |
// static assert forces constant evaluation | |
static_assert(my_atoi("42") == 42); | |
// constexpr forces constant evaluation | |
constexpr unsigned foo = my_atoi("123"); | |
// fails with compile-time error ("non-constexpr function | |
// '__assert_fail' cannot be used in a constant expression"): | |
constexpr unsigned oops = my_atoi("42.3"); | |
// constinit forces constant evaluation | |
constinit unsigned bar = my_atoi("73"); | |
// fails with compile-time error | |
constinit unsigned oops2 = my_atoi("3.14"); | |
// fails during static initialization (at runtime 😱). You | |
// and make my_atoi() `consteval` to prevent this. | |
unsigned big_oops = my_atoi("2.718"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstddef> | |
#include <type_traits> | |
#include <utility> | |
constexpr int max_aggregate_size = 32; // pick a sufficiently large number | |
// Need to exclude copy/move/converting constructors when testing first argument | |
template <class T, class... Ts> | |
concept NotLike = !(std::is_same_v<std::remove_cvref_t<T>, Ts> || ...); | |
template <size_t, class... Ts> | |
struct AnyExcept { | |
template <NotLike<Ts...> T> operator T&() const; | |
template <NotLike<Ts...> T> operator T&&() const; | |
}; | |
template <class T, size_t N> | |
concept ConstructibleWithN = (requires { | |
// Use a lambda to expand an index sequence of size N into a parameter pack | |
[]<size_t I, size_t... Idxs>(std::index_sequence<I, Idxs...>) | |
// If the first argument is convertible to T, it's probably a copy/move | |
// constructor and doesn't tell us about it's aggregate constructibility | |
requires requires { T{AnyExcept<I, T>{}, AnyExcept<Idxs>{}...}; } { | |
}(std::make_index_sequence<N>{}); | |
}) || (N == 0 && requires { T{}; }); | |
template <class T, size_t N> | |
concept AggregateOfN = std::is_aggregate_v<T> | |
&& ConstructibleWithN<T, N> && !ConstructibleWithN<T, N+1>; | |
template <class T> requires std::is_aggregate_v<T> | |
constexpr auto NumAggregateMembers = | |
[]<size_t... Idxs>(std::index_sequence<Idxs...>) { | |
return ((AggregateOfN<T, Idxs> * Idxs) + ... + 0); | |
}(std::make_index_sequence<max_aggregate_size>{}); | |
// ============================================================================= | |
// Example | |
struct Foo { | |
int i; | |
double j; | |
char k; | |
}; | |
static_assert(AggregateOfN<Foo, 3>); | |
static_assert(NumAggregateMembers<Foo> == 3); | |
struct Bar {}; | |
static_assert(AggregateOfN<Bar, 0>); | |
static_assert(NumAggregateMembers<Bar> == 0); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
template <class T, class U> | |
struct Foo { | |
T bar; | |
U baz; | |
}; | |
// Not needing in C++20 | |
template <class T, class U> | |
Foo(T, U) -> Foo<T, U>; | |
int main() { | |
// Type of test is Foo<int, char> | |
auto test = Foo{42, 'a'}; | |
// Type of test2 is Foo<double, const char[6]> | |
auto test2 = Foo{3.14, "hello"}; | |
std::cout << test.bar << std::endl; | |
std::cout << test2.baz << std::endl; | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <vector> | |
template <class T, class U, class V> | |
struct Foo { | |
T bar; | |
std::vector<U> baz; | |
V foobar; | |
}; | |
template <class T, class U, class V> | |
Foo(T, std::initializer_list<U>, V) -> Foo<T, U, V>; | |
int main() { | |
auto test = Foo{.bar = 42, .baz = {3.14, 2.718, 1.618}, .foobar = 'x'}; | |
std::cout << test.baz[1] << std::endl; | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
template <class T, class U> | |
struct Foo { | |
T bar; | |
U baz; | |
}; | |
int main() { | |
auto test = Foo{.bar = 42, .baz = 'a'}; | |
std::cout << test.baz << std::endl; | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <utility> | |
void enumerate_pack(auto f, auto... args) { | |
[&]<size_t... Idxs>(std::index_sequence<Idxs...>) { (f(Idxs, args), ...); } | |
(std::make_index_sequence<sizeof...(args)>{}); | |
} | |
int main() { | |
enumerate_pack( | |
[](std::size_t i, auto arg) { std::cout << i << " : " << arg << '\n'; }, | |
"hello", 42, "world", 73); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <thread> | |
#include <vector> | |
struct ReleaseExclusiveAccessException {}; | |
template <class F> | |
void execute_exclusively(F&& f) { | |
try { | |
static bool run_exclusive = [&] { | |
f(); | |
throw ReleaseExclusiveAccessException{}; | |
return true; // Unreachable | |
}(); | |
} catch (ReleaseExclusiveAccessException) { | |
} | |
} | |
int main() { | |
std::vector<std::thread> ts; | |
int counter = 0; | |
for (int i = 0; i < 8; i++) { | |
ts.emplace_back([&] { | |
for (int i = 0; i < 1000; i++) { | |
execute_exclusively([&] { counter++; }); | |
} | |
}); | |
} | |
for (auto&& t : ts) { | |
t.join(); | |
} | |
std::cout << "Exactly 8000: " << counter << std::endl; | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <thread> | |
#include <vector> | |
void foo() { | |
static bool unused = [] { | |
std::cout << "printed once" << std::endl; | |
return true; | |
}(); | |
} | |
int main() { | |
std::vector<std::thread> ts; | |
for (int i = 0; i < 3; i++) { | |
ts.emplace_back(foo); | |
} | |
for (auto&& t : ts) { | |
t.join(); | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <thread> | |
#include <vector> | |
constexpr auto times_to_run = 3; | |
struct NotDoneInitializingException {}; | |
void foo() { | |
try { | |
static bool unused = [] { | |
static int count = 0; | |
std::cout << "printed " << times_to_run << " times" << std::endl; | |
if (++count < times_to_run) { | |
throw NotDoneInitializingException{}; | |
} | |
return true; | |
}(); | |
(void)unused; | |
} catch (NotDoneInitializingException) { | |
} | |
} | |
int main() { | |
std::vector<std::thread> ts; | |
for (int i = 0; i < 4; i++) { | |
ts.emplace_back([] { | |
for (int i = 0; i < 100; i++) { | |
foo(); | |
} | |
}); | |
} | |
for (auto&& t : ts) { | |
t.join(); | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
template <class... Ts> | |
void print_args_backwards(Ts... ts) { | |
auto print_one = [](auto t) { | |
std::cout << t << std::endl; | |
// Anything with a reasonable assignment operator | |
// will work here | |
return std::type_identity<void>{}; | |
}; | |
std::cout << "*** Backwards ***" << std::endl; | |
(print_one(ts) = ...); | |
std::cout << "*** Forwards ***" << std::endl; | |
(print_one(ts), ...); | |
} | |
int main() { | |
print_args_backwards(1, 2, "hello", 3, 4, "world"); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <utility> | |
template <typename, size_t> | |
concept Any = true; | |
auto invoke_with_last_arg(auto f, auto... args) { | |
return [&]<size_t... Idxs>(std::index_sequence<Idxs...>) { | |
return [f](Any<Idxs> auto..., auto last) { return f(last); }(args...); | |
} | |
(std::make_index_sequence<sizeof...(args) - 1>{}); | |
} | |
void demo(auto... args) { | |
invoke_with_last_arg( | |
[](auto last_arg) { std::cout << last_arg << std::endl; }, args...); | |
} | |
int main() { | |
demo(3.14, 42, "hello world"); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
https://www.youtube.com/watch?v=15etE6WcvBY | |
Why You Should Write Code That You Should Never Write - Daisy Hollman - CppCon 2021 | |
Cute C++ Tricks, Part 2.5 of N - Code You Should Learn From & Never Write - Daisy Hollman - CppCon22 | |
https://www.youtube.com/watch?v=gOdcNko2xc8 | |
https://daisyh.dev/talks/cppcon-2022/what-you-can-learn-from-being-too-cute-part-2-point-5.html | |
https://cute.godbolt.org/z/odsjjanez |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <mutex> | |
#include <thread> | |
#include <vector> | |
template <class F> | |
void execute_exclusively(F&& f) { | |
static std::mutex mtx; | |
std::scoped_lock lock{mtx}; | |
f(); | |
} | |
int main() { | |
std::vector<std::thread> ts; | |
int counter = 0; | |
for (int i = 0; i < 8; i++) { | |
ts.emplace_back([&] { | |
for (int i = 0; i < 1000; i++) { | |
execute_exclusively([&] { counter++; }); | |
} | |
}); | |
} | |
for (auto&& t : ts) { | |
t.join(); | |
} | |
std::cout << "Exactly 8000: " << counter << std::endl; | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <atomic> | |
#include <iostream> | |
#include <mutex> | |
#include <thread> | |
#include <vector> | |
constexpr auto times_to_run = 3; | |
void foo() { | |
static std::atomic<int> times_executed = 0; | |
if (times_executed++ < times_to_run) { | |
static std::mutex mtx; | |
std::scoped_lock lock{mtx}; | |
std::cout << "printed " << times_to_run << " times\n"; | |
} | |
} | |
int main() { | |
std::vector<std::thread> ts; | |
for (int i = 0; i < 4; i++) { | |
ts.emplace_back([] { | |
for (int i = 0; i < 100; i++) { | |
foo(); | |
} | |
}); | |
} | |
for (auto&& t : ts) { | |
t.join(); | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <mutex> | |
#include <thread> | |
#include <vector> | |
constexpr auto times_to_run = 3; | |
struct ReleaseExclusiveAccessException {}; | |
void foo() { | |
static std::once_flag run_once; | |
std::call_once(run_once, [] { | |
std::cout << "printed_once" << std::endl; | |
return true; | |
}); | |
} | |
int main() { | |
std::vector<std::thread> ts; | |
int counter = 0; | |
for (int i = 0; i < 8; i++) { | |
ts.emplace_back(foo); | |
} | |
for (auto&& t : ts) { | |
t.join(); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment