Skip to content

Instantly share code, notes, and snippets.

@vladiant
Last active May 19, 2023 03:02
Show Gist options
  • Save vladiant/c740b52282833b5369a070e0970a7b3b to your computer and use it in GitHub Desktop.
Save vladiant/c740b52282833b5369a070e0970a7b3b to your computer and use it in GitHub Desktop.
Surprising C++
#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;
}
#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;
}
#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");
#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);
#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;
}
#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;
}
#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;
}
#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;
}
#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;
}
#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;
}
#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;
}
#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;
}
#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;
}
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
#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;
}
#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;
}
#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