Reference:
https://lld.llvm.org/#using-lld
$ clang++ -fuse-ld=lld
Symphilosophein 狂者進取,狷者有所不為也。
std::uint32_t decode(const void* p) {
return boost::endian::little_to_native(*static_cast<const std::uint32_t *>(p));
}
Safe:std::uint32_t decode(const void* p) {
std::uint32_t v = 0; // local variable, guaranteed aligned.
std::memcpy(&v, p, sizeof(v));
boost::endian::little_to_native_inplace(v);
return v;
}#include <bit> // Required for std::bit_cast
#include <array> // Required for std::array
#include <cstdint> // Required for std::uint32_t
// ... boost::endian headers ...
std::uint32_t decode(const void* p) {
// 1. Cast 'p' to a pointer to an array of 4 bytes.
// This cast itself is just a reinterpretation; no memory is read.
// Reason cast to const unsigned char* won't work due to
// std::bit_cast has safety check making sure that the sizeof(From) == sizef(To)
// Thus sizeof(const unsigned char*) is just one byte, not 4 bytes.
const auto* byte_ptr = reinterpret_cast<const std::array<unsigned char, 4>*>(p);
// 2. Dereference the pointer.
// This performs a *safe* copy of 4 bytes from the (potentially unaligned)
// address 'p' into a local, *aligned* 'bytes' object.
std::array<unsigned char, 4> bytes = *byte_ptr;
// 3. Reinterpret the bits of the byte array as a uint32_t.
std::uint32_t v = std::bit_cast<std::uint32_t>(bytes);
// 4. Fix endianness, same as before.
boost::endian::little_to_native_inplace(v);
return v;
}float get_first_element(__m128 v)
{
return _mm_cvtss_f32(v);
}
_Z17get_first_elementDv4_f:
.LFB6474:
.cfi_startproc
endbr64
ret
.cfi_endproc// get the epoch
auto epoch =
std::chrono::system_clock::now().time_since_epoch();
// send it to a peer
send_network(epoch);
// align on the struct, not the data members, thus false sharing still happening.
struct alignas(64) my_struct {
std::atomic<int> one;
std::atomic<int> two;
}; struct my_struct {
alignas(std::hardware_destructive_interference_size) std::atomic<int> one;
alignas(std::hardware_destructive_interference_size) std::atomic<int> two;
}; Signal-driven I/O is rarely used in modern applications because its disadvantages generally outweigh its benefits. Newer APIs like epoll (Linux), kqueue (BSD/macOS), and IOCP (Windows) are far superior.
Complexity of Signal Handling: Dealing with signals is notoriously difficult and error-prone. Signal handlers have many restrictions (e.g., only a limited set of functions, known as async-signal-safe functions, can be safely called from within them).
Unreliable Signal Queuing: Signals are not queued. If two I/O events occur in rapid succession, the kernel might only deliver a single SIGIO signal. This means the signal handler must be written in a loop to read (or write) until the operation would block (returning an EWOULDBLOCK or EAGAIN error). Forgetting this leads to lost I/O events.
Complexity in Multithreading: Signal handling in a multithreaded program is extremely complex. It's often unclear which thread will receive the signal, leading to difficult synchronization problems.
Still a Synchronous Model: According to the POSIX standard, signal-driven I/O is still a synchronous I/O model. While the notification is asynchronous, the actual I/O call (e.g., recvfrom()) is initiated by the process and can still block in the signal handler or main loop. This is different from true asynchronous I/O (AIO), where the kernel performs the entire operation (including the data copy) and only notifies the process upon completion.
#include <functional>
#include <iostream>
#include <optional>
template <size_t I, typename F>
constexpr std::optional<int> ApplyIndexForConstexpr(F f) {
if constexpr(sizeof(F) == 1){
return I;
}
if constexpr(I - 1 == 0){
return std::nullopt;
} else {
return ApplyIndexForConstexpr<I-1>(f);
}
}
template <size_t I, typename F>
constexpr std::optional<int> ApplyIndexForConstexprElse(F f) {
if constexpr(sizeof(F) == 1){
return I;
} else if constexpr(I - 1 == 0){
return std::nullopt;
} else {
return ApplyIndexForConstexpr<I-1>(f);
}
}
int main() {
auto run = []{};
ApplyIndexForConstexpr<2>(run);
ApplyIndexForConstexprElse<2>(run);
}
Generated code:
#include <functional>
#include <iostream>
#include <optional>
template<size_t I, typename F>
inline constexpr std::optional<int> ApplyIndexForConstexpr(F f)
{
if constexpr(sizeof(F) == 1) {
return std::optional<int>(I);
}
if constexpr((I - 1) == 0) {
return std::optional<int>(std::nullopt_t(std::nullopt));
} else /* constexpr */ {
return ApplyIndexForConstexpr<I - 1>(f);
}
}
/* First instantiated from: insights.cpp:31 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr std::optional<int> ApplyIndexForConstexpr<2, __lambda_30_13>(__lambda_30_13 f)
{
if constexpr(true) {
return std::optional<int>(2UL);
}
if constexpr(false) {
} else /* constexpr */ {
return ApplyIndexForConstexpr<2UL - 1>(__lambda_30_13(f));
}
}
#endif
/* First instantiated from: insights.cpp:14 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr std::optional<int> ApplyIndexForConstexpr<1, __lambda_30_13>(__lambda_30_13 f)
{
if constexpr(true) {
return std::optional<int>(1UL);
}
if constexpr(true) {
return std::optional<int>(std::nullopt_t(std::nullopt));
} else /* constexpr */ {
}
}
#endif
template<size_t I, typename F>
inline constexpr std::optional<int> ApplyIndexForConstexprElse(F f)
{
if constexpr(sizeof(F) == 1) {
return std::optional<int>(I);
} else /* constexpr */ {
if constexpr((I - 1) == 0) {
return std::optional<int>(std::nullopt_t(std::nullopt));
} else /* constexpr */ {
return ApplyIndexForConstexpr<I - 1>(f);
}
}
}
/* First instantiated from: insights.cpp:32 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
inline constexpr std::optional<int> ApplyIndexForConstexprElse<2, __lambda_30_13>(__lambda_30_13 f)
{
if constexpr(true) {
return std::optional<int>(2UL);
} else /* constexpr */ {
}
}
#endif
int main()
{
class __lambda_30_13
{
public:
inline /*constexpr */ void operator()() const
{
}
using retType_30_13 = auto (*)() -> void;
inline constexpr operator retType_30_13 () const noexcept
{
return __invoke;
};
private:
static inline /*constexpr */ void __invoke()
{
__lambda_30_13{}.operator()();
}
public:
// inline /*constexpr */ __lambda_30_13(const __lambda_30_13 &) noexcept = default;
};
__lambda_30_13 run = __lambda_30_13{};
ApplyIndexForConstexpr<2>(__lambda_30_13(run));
ApplyIndexForConstexprElse<2>(__lambda_30_13(run));
return 0;
}
godbolt:
https://godbolt.org/z/Yenv16xzj
#include <functional>
#include <iostream>
#include <optional>
template <size_t I, typename F>
constexpr std::optional<int> ApplyIndexForOverflow(F f) {
return ApplyIndexForOverflow<I - 1>(f);
}
template <size_t I, typename F>
constexpr std::optional<int> ApplyIndexFor(F f) {
if (I == 0) {
return std::nullopt;
}
// double checked stop; otherwise introduced stack overflow
// from the compiler runtime due to has to instantiate
// unbounded template instance, like above `ApplyIndexForOverflow`
return ApplyIndexFor<(I == 0 ? 0 : I - 1 )>(f);
}
template <size_t I, typename F>
constexpr std::optional<int> ApplyIndexForConstexpr(F f) {
if constexpr(sizeof(F) == 1){
return I;
}
if constexpr(I - 1 == 0){
return std::nullopt;
} else {
return ApplyIndexForConstexpr<I-1>(f);
}
}
int main() {
auto run = []{};
ApplyIndexForOverflow<100>(run);
ApplyIndexFor<100>(run);
ApplyIndexForConstexpr<100>(run);
}
#include <iostream>
#include <vector>
#include <numeric>
#include <variant>
#include <functional>
#include <utility>
// --- (Paste the Bounce, Step, and trampoline definitions from above here) ---
template<typename T>
struct Bounce;
template<typename T>
using Step = std::variant<T, Bounce<T>>;
template<typename T>
struct Bounce {
std::function<Step<T>()> thunk;
};
template<typename T>
T trampoline(Step<T> first_step) {
Step<T> current_step = std::move(first_step);
while (std::holds_alternative<Bounce<T>>(current_step)) {
current_step = std::get<Bounce<T>>(current_step).thunk();
}
return std::get<T>(current_step);
}
// --- (Paste the sum_trampolined function from above here) ---
Step<long> sum_trampolined(const std::vector<long>& data, size_t index, long current_sum) {
if (index == data.size()) {
return current_sum;
}
return Bounce<long>{
[=]() {
return sum_trampolined(data, index + 1, current_sum + data[index]);
}
};
}
int main() {
// This will now work without crashing!
std::vector<long> large_vec(200000, 1);
// To start the process, we create the very first step.
Step<long> first_step = sum_trampolined(large_vec, 0, 0);
// The trampoline function runs the computation to completion.
long total = trampoline(first_step);
std::cout << "Trampolined sum of large vector: " << total << std::endl;
std::cout << "The program finished successfully." << std::endl;
return 0;
}