Showing posts with label cpp_attribute. Show all posts
Showing posts with label cpp_attribute. Show all posts

Dec 15, 2025

[C++] [CppCon 2025] Implement the C++ Standard Library minute

Reference:

Implement the C++ Standard Library: Design, Optimisations, Testing while Implementing Libc++
[C++] Object Lifetimes reading minute

1)

1) tail padding.
2) if type is empty, it does not acquire address.

e.g.
struct Foo {
  int a; // 4
  char c; // 1 
  bool b; // 1
  // +2
};

enum class ErrCode : int {
 E1, E2, E3,
};

template<typename Val, Err>
struct Expected {
  union U {
  	[[no_unique_address]] Val val_;
	[[no_unique_address]] Err err_;
  };
  [[no_unique_address]] U value_;
  bool has_value_; 
};
Without `no_unique_address`, no tail padding,
sizeof(Expected<Foo, ErrCode>) == 12

With `no_unique_address`, 
bool has_value_;
can sneak into
U if U is of type Foo, `has_value_` would occupy Foo's tail padding,
thus:
sizeof(Expected<Foo, ErrCode>) == 8


2)

template<typename Val, Err>
struct Expected {
  union U {
  	[[no_unique_address]] Val val_;
	[[no_unique_address]] Err err_;
  };
  [[no_unique_address]] U value_;
  bool has_value_; 
  
  Expected(const Expected& other) :
  	has_value{other.has_value} {
	 if (has_value_) {
	 	std::construct_at(std::addressof(value_.val_), other.value_.val_);
	 } else {
	 	std::construct_at(std::addressof(value_.err_), other.value_.err_);
	 }
	}
};

Expected e1 = {Foo{}, true};
Expected e2 = e1;
assert(e2.has_value_); // Failed.
Why failed? Due to std::construct_at construct the value_ union's val_ and
zero out the padding, which has_value_ resides in due to `no_unique_address`.

Take away:

Don't mix no_unique_address with manual lifetime management
(union, construct_at, placement-new, std::bit_cast, std::start_lifetime_as, std::start_lifetime_as_array).


3)

Reference:
Segmented Iterators and Hierarchical Algorithms - Matthew H. Austern
https://lafstern.org/matt/segmented.pdf
Which is faster?
std::deque<int> d = ...

// 1
for (int& i : d) {
    i = std::clamp(i, 200, 500);
}

// 2 Faster due to using Segmented Iterators
std::ranges::for_each(d, [](int& i) {
    i = std::clamp(i, 200, 500);
});

Segmented Iterators:

template <class T> struct seg_array_iter
{
    // Local Iterator: position within the current segment (e.g., a vector<T>::iterator)
    vector<T>::iterator cur;
    
    // Segment Iterator: position among the segments (e.g., a vector<vector<T>*>::iterator)
    vector<vector<T>*>::iterator node;

    T& operator*() const { return *cur; }

    seg_array_iter& operator++() {
        // Standard increment: move to the next element in the current segment
        if (++cur == (**node).end()) {
            // End of segment reached: move to the next segment
            ++node;
            // Set the local iterator (cur) to the beginning of the new segment
            // or 0 (a null pointer equivalent for iterators) if it's the end.
            cur = *node ? (**node).begin() : 0;
        }
        return *this;
    }
    // ... other iterator operations like operator==, operator!=, etc.
};

template <class Iter, class T>
inline void fill (Iter first, Iter last, const T& x)
{
    typedef segmented_iterator_traits<Iter> Traits;
    
    // Dispatch call: The compiler chooses the segmented or nonsegmented helper 
    // based on the type of Traits::is_segmented_iterator (true_type or false_type).
    fill(first, last, x,
         typename Traits::is_segmented_iterator()); 
}

template <class SegIter, class T>
void fill (SegIter first, SegIter last, const T& x, true_type)
{
    typedef segmented_iterator_traits<SegIter> traits;

    // Decompose the full segmented iterators into segment and local iterators
    typename traits::segment_iterator sfirst = traits::segment (first);
    typename traits::segment_iterator slast = traits::segment (last);

    if (sfirst == slast) {
        // Case 1: Start and end are in the same segment. Use one local fill.
        fill(traits::local (first), traits::local (last), x);
    } else {
        // Case 2: Fill the *rest* of the starting segment
        fill(traits::local (first), traits::end(sfirst), x);

        // Case 3: Loop through all *middle* segments, filling them completely
        for (++sfirst; sfirst != slast; ++sfirst)
            fill(traits::begin(sfirst), traits::end(sfirst), x);

        // Case 4: Fill the *start* of the ending segment (sfirst now equals slast)
        fill(traits::begin(sfirst), traits::local (last), x);
    }
} 

Take away:

  • Use the most precise API for what you're trying to achieve.
    • insert_range instead of insert in a loop. (O(n) vs. N * log(M) )
    • use sorted_unique if the inputs are already sorted.
  • Use library facilities (e.g. views::zip) to benefit from concept-based optimizations.


4)

compiler flag
-Xclang -verify used for static_asserts
e.g.
clang++ -Xclang -verify -fsyntax-only test_integer_only.cpp

#include <type_traits>

template <typename T>
void strict_int_check() {
    static_assert(std::is_integral<T>::value, "Type must be an integer!");
}

void test() {
    // This line is valid, so no comment needed.
    strict_int_check<int>();

    // We WANT this line to fail. 
    // Without -verify, this breaks the build.
    // With -verify, Clang checks if the error matches the regex in {{...}}.
    strict_int_check<double>(); // expected-error {{Type must be an integer!}}
}

Jan 26, 2021

[C++17] something about attribute

[[nodiscard]]

Should usually be used to signal misbehavior when return values are not used. The misbehavior might be:

• Memory leaks, such as not using returned allocated memory.

• Unexpected or non-intuitive behavior such as getting different/unexpected behavior when not using the return value.

• Unnecessary overhead, such as calling something that is a no-op if the return value is not used.


[[maybe_unused]]

Can be used to avoid warnings by the compiler for not using a name or entity.


[[fallthrough]]

Can be used to avoid warnings by the compiler for not having a break statement after a sequence of one or more case labels inside a switch statement.


General Attribute Extensions:

  1. Attributes can now be used to mark namespaces.
    namespace [[deprecated]] DraftAPI {}
    
  2. Attributes can now be used to mark enumerators (values of enumeration types).
  3. For user-defined attributes, which should usually be defined in their own namespace, you can now use a using prefix to avoid the repetition of the attribute namespace for each attribute. That is, instead of: 
    [[MyLib::WebService, MyLib::RestService, MyLib::doc("html")]] void foo();
    

you can just write

[[using MyLib: WebService, RestService, doc("html")]] void foo();


Jan 1, 2019

[C++][C] The case of forgotten return bug

read: https://yurichev.com/blog/no_return/

Scenarios:
  • Even without return variable from a function signature which returns, %rax is filled with
    something due to compiler will use as less registers as possible.
    Since %rax is filled with something which is not the returning value, this issues a bug
    (in debug/none-optimized mode).
  • While in optimization mode, optimize prevails, thus a function with signature which returns but inside the function body has no return statement, compiler opts out with returning 0, which again, issues a bug.


Take away:
  • Always turns on -Wall during the programming(I'm using Vim) and compiling.
  • In C++17, consider using attribute: [[nodiscard]] a good practice.
    For side-effect only, take Discarded-value expressions into consideration.


#include <stdio.h>
#include <stdlib.h>

struct color
{
        int R;
        int G;
        int B;
};

struct color* create_color (int R, int G, int B)
{
        struct color* rt=(struct color*)malloc(sizeof(struct color));
        rt->R=R;
        rt->G=G;
        rt->B=B;
        // must be "return rt;" here
};

int main()
{
        struct color* a=create_color(1,2,3);
        printf ("%d %d %d\n", a->R, a->G, a->B);
};

https://godbolt.org/z/o8bgyD
create_color(int, int, int):
pushq %rbp
movq %rsp, %rbp
subq $32, %rsp
movl %edi, -20(%rbp)
movl %esi, -24(%rbp)
movl %edx, -28(%rbp)
movl $12, %edi
call malloc
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movl -20(%rbp), %edx
movl %edx, (%rax)
movq -8(%rbp), %rax
movl -24(%rbp), %edx
movl %edx, 4(%rax)
movq -8(%rbp), %rax
movl -28(%rbp), %edx
movl %edx, 8(%rax)
nop
leave
ret
.LC0:
.string "%d %d %d\n"
main:
pushq %rbp
movq %rsp, %rbp
subq $16, %rsp
movl $3, %edx
movl $2, %esi
movl $1, %edi
call create_color(int, int, int)
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movl 8(%rax), %ecx
movq -8(%rbp), %rax
movl 4(%rax), %edx
movq -8(%rbp), %rax
movl (%rax), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
ret