- random c++ stuff that might be leeched off the internet
- std::shared_ptr<void>
- writing an allocator
- generating switch statements with templates
- counting the number of member fields of a trivial struct
- OmegaException
- destructors should almost never throw exceptions
- defining a static class member
- Probably prefer
(... <op> Ts)to(Ts <op> ...>) - type hack
- Separator argument
- delete move constructor
- strange syntax
- getting the offset of a base class from a derived class
- Calendar tricks
random c++ stuff that might be leeched off the internet
std::shared_ptr<void>
since shared_ptr stores a type-erased deleter, you can do the following
void foo() {
std::shared_ptr<void> p(new A);
p.reset(new B); // ~A() called here
} // ~B() called here
writing an allocator
signature of an allocator that will work with std::vector<int, myalloc<int>>
template <typename T>
struct myalloc {
using value_type = T;
T* allocate(size_t n) {...}
void deallocate(T*, size_t n) {...}
// probably delete copy ctor and copy assign too
};
generating switch statements with templates
from my stack overflow question
template <int ...Key>
void foo(int key) {
switch (key) {
case 1: bar<1>();break;
case 3: bar<3>();break;
case 5: bar<5>();break;
case 7: bar<7>();break;
case 9: bar<9>();break;
default: break;
}
}
can be re-written as
template <int ...Keys>
void foo(int key) {
([&]() {
if (key == Keys) {
bar<Keys>();
return true;
}
return false;
}() || ...);
}
counting the number of member fields of a trivial struct
struct X {
int a;
int b;
};
struct OmegaType {
template <typename T>
operator T() {}
};
template <typename T>
constexpr size_t count_member(auto... args) {
if constexpr (requires {T{args...};} == false) {
return sizeof...(args) - 1;
} else {
return count_member<T>(args..., OmegaType{});
}
}
static_assert(count_member<X>() == 2);
OmegaException
From cppcon
#include <string>
#include <source_location>
#include <iostream>
template <typename DATA_T>
class OmegaException {
public:
OmegaException(
std::string str,
DATA_T data,
const std::source_location& loc = std::source_location::current()
// std::stacktrace trace = std::stacktrace::current()
) :
err_str_{std::move(str)},
user_data_{std::move(data)},
location_(loc)
// backtrace_(trace)
{}
std::string& what() {return err_str_;}
const std::string& what() const noexcept { return err_str_; }
const std::source_location& where() const noexcept { return location_; }
// const std::stacktrace& stack() const noexcept { return backtrace_; }
DATA_T& data() { return user_data_; }
const DATA_T& data() const noexcept { return user_data_; }
private:
std::string err_str_;
DATA_T user_data_;
const std::source_location location_;
// const std::stacktrace backtrace_;
};
int main() {
try {
throw OmegaException<int>("oh no", 5);
} catch (const OmegaException<int>& e) {
std::cout << e.where().file_name() << std::endl;
}
}
destructors should almost never throw exceptions
If an exception is thrown during an exception handling, std::terminate is triggered. Objects are popped off the stack and destructed during stack rewinding, and if one of these puppies throw, your program dies! See isocpp faq.
defining a static class member
A declaration of a struct variable within the struct definition don’t define the struct. An external definition is required.
struct S {
static int X = 5; // declares, not defines
};
int S::X; // defines
int main() {
int y = S::X; // S::X is not not odr-use
v.push_back(S::X); // push_back takes a reference - S::X is odr-use, requires a definition
}
Probably prefer (... <op> Ts) to (Ts <op> ...>)
if op isn’t a commutative operator, (Ts <op> ...) probably don’t do what you expect.
template<int... Ts>
int foo() {
return (Ts - ...);
}
template<int... Ts>
int bar() {
return (... - Ts);
}
// foo<10,2,3>() -> 11
// bar<10,2,3>() -> 5
type hack
Sadly the commitee hated this and swore to break this some day.
#include <string>
#include <type_traits>
template<int N> struct tag{};
template<typename T, int N>
struct loophole_t {
friend auto loophole(tag<N>) { return T{}; };
};
auto loophole(tag<0>);
auto loophole(tag<1>);
int main() {
sizeof( loophole_t<std::string, 0> );
sizeof( loophole_t<int, 1> );
static_assert(std::is_same_v<std::string, decltype( loophole(tag<0>{}) ) >);
static_assert(std::is_same_v<int, decltype( loophole(tag<1>{}) ) >);
}
Separator argument
Have a function that takes in a char as separator? Make it a template arument to save one register!
template <char c>
void foo(const std::vector<std::string>& v) {
// <some concat logic maybe?>
}
delete move constructor
This is an extension of the rule of 5. Specifically, X WILL NOT generate a move constructor but Y will.
struct X {
virtual ~X() = default;
// no move ctor generated due to user defined dtor.
};
struct Y : X {
// move ctor is generated since there is no user defined dtor.
};
strange syntax
void foo(int fn(double)) // fn is a pointer-to-function
getting the offset of a base class from a derived class
struct A {
int a1, a2, a3;
};
struct B {
int b;
};
struct C : A, B {
int c, c1;
};
template <typename Parent, typename Child>
consteval size_t getoffset() {
union {Child c; char b[sizeof(Child)];} u;
auto* a1 = (static_cast<void*>(std::addressof(static_cast<Parent&>(u.c))));
for (int i = 0; i < sizeof(Child); ++i) {
auto* a2 = (static_cast<void*>(std::addressof(u.b[i])));
if (a1 == a2) {
return i;
}
}
}
static_assert(getoffset<A, C>() == 0);
static_assert(getoffset<B, C>() == 12);
Calendar tricks
days in month
// compute day of a month
days_in_month = [](int month) { return ((month > 3) ^ month) & 30; };
// compute day of a month
is_leap_year = [](int year) -> bool { return (year % 25) == 0 ? (year % 16) == 0 : (year % 4) == 0; };
// perform year % 25 first since its easily predictable
// if so: check if its divisible by 16
// else: simply check if its divisible by 4
// these should compile to
// year % 25 // converted to an imul, add and cmp, which is relatively cheap (5 cycles)
// ? : // converted to cmov