Showing posts with label stackoverflow. Show all posts
Showing posts with label stackoverflow. Show all posts

Oct 21, 2023

[C++20] Nicolai M. Josuttis's C++20 the complete guide reading minute - operator <=>

Comparisons and Operator <=>

Reference:

C++20 compiler rewrites
  1. Operator != with !(a==b)
  2. If above doesn't work, change the order of the operands. !(b==a)
And a, b can be different types.
If there's a free-standing operator:
  • A free-standing operator!=(TypeA, TypeB)
  • A free-standing operator==(TypeA, TypeB)
  • A free-standing operator==(TypeB, TypeA)
  • A member function TypeA::operator!=(TypeB)
  • A member function TypeA::operator==(TypeB)
  • A member function TypeB::operator==(TypeA)
Compiler can do the rewrite trick.


Thus To compile
x != y
the compiler might now try all of the following:
x.operator!=(y) // calling member operator!= for x
operator!=(x, y) // calling a free-standing operator!= for x and y
!x.operator==(y) // calling member operator== for x
!operator==(x, y) // calling a free-standing operator== for x and y
!x.operator==(y) // calling member operator== generated by operator<=> for x
!y.operator==(x) // calling member operator== generated by operator<=> for y
The last form is tried to support an implicit type conversion for the first operand, which requires that the operand is a parameter.

In general, the compiler tries to call:
  • A free-standing operator !=: operator!=(x, y)
  • or a member operator !=: x.operator!=(y)
Having both operators != defined is an ambiguity error.

  • A free-standing operator ==: !operator==(x, y)
  • or a member operator ==: !x.operator==(y)
Note that the member operator == may be generated from a defaulted operator<=> member.
Having both operators == defined is an ambiguity error.
This also applies if the member operator== is generated due to a defaulted operator<=>.

When an implicit type conversion for the first operand v is necessary, 
the compiler also tries to reorder the operands. Consider:
42 != y // 42 implicitly converts to the type of y
In that case, the compiler tries to call in that order:
  • A free-standing or member operator !=
  • A free-standing or member operator == (note that the member operator == may be generated from a defaulted operator<=> member)
Note that a rewritten expression never tries to call a member operator !=


Thus To compile
x <= y
new operator <=> and compare the result with 0. 
The operator behaves like a three-way comparison function
returning a negative value for less, 0 for equal, and a positive value for greater 
(the returned value is not a numeric value; it is only a value that supports the corresponding comparisons).
The compiler might now try all of the following:
x.operator<=(y) // calling member operator<= for x
operator<=(x, y) // calling a free-standing operator<= for x and y
x.operator<=>(y) <= 0 // calling member operator<=> for x
operator<=>(x, y) <= 0 // calling a free-standing operator<=> for x and y
0 <= y.operator<=>(x) // calling member operator<=> for y
The last form is tried to support an implicit type conversion for the first operand,
for which it has to become a parameter.


Operator <=>

  • The return of <=> operator type should be marked as 'auto' and let compiler to deduce the type.
  • Operator <=> takes precedence over all other comparison operators; except explicitly user defined.
  • Should only call operator <=> directly when implementing operator<=>.
    However, it can be very helpful to know the returned comparison category.
#include <compare>
// order of the members in the class matters.
class Value {
	// defines the ordering and can be used by the relational operators <, <=, >, and >=.
	auto operator<=> (const Value& rhs) const = default;
	// implicitly generated
	// defines equality and can be used by the equality operators == and !=.
	auto operator== (const Value& rhs) const = default; 
};

class Value {
  private:
  	long id;

  public:
	constexpr Value(long i) noexcept
		: id{i} {}

	// for equality operators:
	bool operator== (const Value& rhs) const {
	  return id == rhs.id; // defines equality (== and !=)
	}

	// for relational operators:
	auto operator<=> (const Value& rhs) const {
	  return id <=> rhs.id; // defines ordering (<, <=, >, and >=)
	}
};

Compiler generated operator has following traits

  • They are noexcept if comparing the members never throws 
  • They are constexpr if comparing the members is possible at compile time 
  • Thanks to rewriting, implicit type conversions for the first operand are also supported (This can also be tricky/buggy)

C++20 compiler rewrites
If no operator<=:
x <= y
rewrites with:
  (x <=> y) <= 0;
  // Or:
  0 <= (y <=> x);
  • If the value of x<=>y is equal to 0, x and y are equal or equivalent.
  • If the value of x<=>y is less than 0, x is less than y.
  • If the value of x<=>y is greater than 0, x is greater than y.
However, note that the return type of operator<=> is not an integral value.
Return type is a type that signals the comparison category, which could be 
  • strong ordering, 
  • weak ordering, 
  • or partial ordering.
These types support the comparison with 0 to deal with the result.

Note that operator<=> is for implementing types. Outside the implementation of an operator<=>,
programmers should never invoke <=> directly. Although you can, you should never write 
a<=>b < 0
instead of
a<b

Comparison Category Types

strong ordering (total ordering):

– std::strong_ordering::less
– std::strong_ordering::equal
(also available as std::strong_ordering::equivalent)
– std::strong_ordering::greater
Any value of a given type is less than or equal to or
greater than any other value of this type (including itself).

weak ordering:

– std::weak_ordering::less
– std::weak_ordering::equivalent
– std::weak_ordering::greater
Any value of a given type is less than or equivalent to or greater than any other
value of this type (including itself). However, equivalent values do not have to be equal
 (have the same value).

E.g. "hello" is equivalent to "HELLO"

– std::partial_ordering::less
– std::partial_ordering::equivalent
– std::partial_ordering::greater
– std::partial_ordering::unordered
Any value of a given type could be less than or equivalent to or greater than any
other value of this type (including itself). 
However, in addition, it may not be possible to specify a specific order between two values at all.

E.g. floating-point types, because they might have the special value
NaN (“not a number”). Any comparison with NaN yields false. Therefore, in this case a comparison
might yield that two values are unordered and the comparison operator might return one of four values.

std::strong_ordering operator<=> (MyType x, MyOtherType y)
{
  if (xIsEqualToY) return std::strong_ordering::equal;
  if (xIsLessThanY) return std::strong_ordering::less;
  return std::strong_ordering::greater;
}

class MyType {
  std::strong_ordering operator<=> (const MyType& rhs) const {
    return value == rhs.value ? std::strong_ordering::equal :
      value < rhs.value ? std::strong_ordering::less :
      std::strong_ordering::greater;
  }
  type value;
};

// often
class MyType {
  auto operator<=> (const MyType& rhs) const {
    return value <=> rhs.value;
  }
  type value;
};

C++20 compiler rewrites
if (!(x < y || y < x)) // might call operator<=> to check for equality
if (x <= y && y <= x) // might call operator<=> to check for equality 

Operator <=> return type mismatch due to multiple data members:


class Person {
std::string name;
double value;

  std::partial_ordering operator<=> (const Person& rhs) const { // OK
    auto cmp1 = name <=> rhs.name;
    if (cmp1 != 0) return cmp1; // strong_ordering converted to return type
    return value <=> rhs.value; // partial_ordering used as the return type
  }
};

// better
class Person {
std::string name;
double value;

  auto operator<=> (const Person& rhs) const 
	-> std::common_comparison_category_t<decltype(name <=> rhs.name),
	decltype(value <=> rhs.value)> {
    auto cmp1 = name <=> rhs.name;
    if (cmp1 != 0) return cmp1; // used as or converted to common comparison type
    return value <=> rhs.value; // used as or converted to common comparison type
  }
};

// convert to same comparison category:
class Person {
std::string name;
double value;

  std::strong_ordering operator<=> (const Person& rhs) const {
    auto cmp1 = name <=> rhs.name;
    if (cmp1 != 0) return cmp1; // return strong_ordering for std::string
    // map floating-point comparison result to strong ordering:
    // https://en.cppreference.com/w/cpp/utility/compare/strong_order
    return std::strong_order(value, rhs.value);
  }
};


std::strong_order() yields a std::strong_ordering value according to the passed arguments as follows:

  • Using std::strong_order(val1, val2) for the passed types if defined
  • Otherwise, if the passed values are floating-point types, using the value of totalOrder() as specified in ISO/IEC/IEEE 60559 (for which, e.g., -0 is less than +0 and -NaN is less than any non-NAN value and +NaN) 
  • Using the new function object std::compare_three_way{}(val1, val2) if defined for the passed types std::compare_three_way use like std::less 

For other types that have a weaker ordering and operators == and < defined, you can use the function
```
Performs three-way comparison on subexpressions t and u and produces a result of type std::strong_ordering, even if the operator <=> is unavailable.
```
accordingly:
class Person {
std::string name;
SomeType value;

  std::strong_ordering operator<=> (const Person& rhs) const {
    auto cmp1 = name <=> rhs.name;
    if (cmp1 != 0) return cmp1; // return strong_ordering for std::string
    // map weak/partial comparison result to strong ordering:
    return std::compare_strong_order_fallback(value, rhs.value);
  }
};

Defaulted operator== and operator<=> contract:

Defaulted operator<=> implies Defaulted operator==
Thus the following is enough to support all six comparison operators for objects of the type Coord:

#include <compare>
struct Coord {
  double x{};
  double y{};
  double z{};
  auto operator<=>(const Coord&) const = default;
};

The second parameter as const lvalue reference (const &) in member function. 
Friend functions might alternatively take both parameters by value.

  • The defaulted operators require the support of the members and possible base classes
  • Defaulted operators == require the support of == in the members and base classes.
  • Defaulted operators <=> require the support of == and either an implemented operator < or a defaulted operator <=> in the members and base classes.
  • The operator is noexcept if comparing the members guarantees not to throw.
  • The operator is constexpr if comparing the members is possible at compile time.

For empty classes, the defaulted operators compare all objects as equal: 
  • operators ==, <=, and >= yield true, 
  • operators !=, <, and > yield false, 
  • and <=> yields std::strong_ordering::equal.
template<typename T>
class Type {
  public:
	[[nodiscard]] virtual std::strong_ordering
		operator<=>(const Type&) const requires(!std::same_as<T,bool>) = default;
};

// compiler generates equivalent to
template<typename T>
class Type {
  public:
	[[nodiscard]] virtual std::strong_ordering
		operator<=> (const Type&) const requires(!std::same_as<T,bool>) = default;
    [[nodiscard]] virtual bool
		operator== (const Type&) const requires(!std::same_as<T,bool>) = default;
};

Implementation of the Defaulted operator<=>:

Contract:
If operator<=> is defaulted and you have members or base classes and you call one of the relational
operators, then the following happens:
  • If operator<=> is defined for a member or base class, that operator is called.
  • Otherwise, operator== and operator< are called to decide whether (from the point of view of the members or base classes)
– The objects are equal/equivalent (operator== yields true)
– The objects are less or greater
– The objects are unordered (only when partial ordering is checked)
In this case, the return type of the defaulted operator<=> calling these operators cannot be auto.
For example, consider the following declarations:
struct B {
	bool operator==(const B&) const;
	bool operator<(const B&) const;
};

struct D : public B {
  // return type can not be auto due to base type has the operator== and operator< defined
  // because it cannot decide which ordering category the base class has. 
  // In that case, you need operator<=> in the base class too.
  std::strong_ordering operator<=> (const D&) const = default;

  // auto generated by compiler even
  // operator<=> is declared as
  // auto operator<=> (const D&) const = default;
  // which then d1 > d2; does't work but d1 != d2; works.
  bool operator== (const D&) const = default;  
};

// Then:
D d1, d2;
d1 > d2; // calls B::operator== and possibly B::operator<

// If operator== yields true, we know that the result of > is false and that is it. 
// Otherwise, operator< is called to find out whether the expression is true or false.


Compare values of a generic type

Defines a total order for raw pointers.
For forward declare operator<=>() result type; use std::compare_three_way_result_t

template<typename T>
struct Value {
  T val{};
...
  auto operator<=> (const Value& v) const noexcept(noexcept(val<=>val)) {
     return std::compare_three_way{}(val<=>v.val);
  }
};

template<typename T>
struct Value {
  T val{};
...
  std::compare_three_way_result_t<T,T>
    operator<=> (const Value& v) const noexcept(noexcept(val<=>val));
};



Appendix

namespace detail
{
    template <unsigned int>
    struct common_cmpcat_base      { using type = void; };
    template <>
    struct common_cmpcat_base <0u> { using type = std::strong_ordering; };
    template <>
    struct common_cmpcat_base <2u> { using type = std::partial_ordering; };
    template <>
    struct common_cmpcat_base <4u> { using type = std::weak_ordering; };
    template <>
    struct common_cmpcat_base <6u> { using type = std::partial_ordering; };
} // namespace detail
 
template <class...Ts>
struct common_comparison_category :
    detail::common_cmpcat_base <(0u | ... |
        (std::is_same_v <Ts, std::strong_ordering>  ? 0u :
         std::is_same_v <Ts, std::weak_ordering>    ? 4u :
         std::is_same_v <Ts, std::partial_ordering> ? 2u : 1u)
    )> {};

Jul 21, 2022

[C++] check container been moved

Reference:
https://stackoverflow.com/questions/29294316/check-if-stdmove-is-done-on-container
n3264

https://g.co/gemini/share/a9f2b627e127

  • moved-from shared_ptrs are guaranteed empty
  • containers can be "emptier than empty" concept
  • user-defined types can be created as weaker concept
  • std::containers should be strict concept; i.e after move .empty() should return 'true'

Mar 11, 2022

[C++] new syntax parsing

Reference:
https://stackoverflow.com/questions/71380971/why-is-new-int-10-wrong
https://en.cppreference.com/w/cpp/language/new


Doesn't work:
auto p = new int (*)[10]; // error: parsed as (new int) (*[10]) ()

Ok:
typedef int array[10];
auto p = new array *;
Ok:
new (int (*[10])()); // okay: allocates an array of 10 pointers to functions

Reasoning:
Syntax for new without initializer is either
new (type)

or
new type

Mar 9, 2022

[C++] Largest value representable by a floating-point type smaller than 1

Reference:
https://stackoverflow.com/questions/71383519/largest-value-representable-by-a-floating-point-type-smaller-than-1

https://en.cppreference.com/w/cpp/numeric/math/nextafter


Question

Obtain the greatest value representable by the floating-point type float which is smaller than 1.

Ans

Portable solution(regardless of what floating-point format your C++ implementation uses. (Binary vs. decimal, or width of mantissa aka significand, or anything else.)), using std::nextafter
#include <iostream>
#include <iomanip>
#include <cmath>
#include <limits>

int main()
{
    double naft = std::nextafter(1.0, 0.0);
    std::cout << std::fixed << std::setprecision(20);
    std::cout << naft << '\n';
    double neps = 1.0 - std::numeric_limits<double>::epsilon();
    std::cout << neps << '\n';
    return 0;
}

Feb 24, 2022

[kernnel][C][C++] ternary conditional operator trick in action

Reference:
Return type of '?:' (ternary conditional operator): https://stackoverflow.com/questions/8535226/return-type-of-ternary-conditional-operator

__is_constexpr() macro is dark magic: https://lore.kernel.org/linux-hardening/20220131204357.1133674-1-keescook@chromium.org/?fbclid=IwAR0Rgg_tGDk0qiEyuDsuZwERITSdstxmU2-bOtadb7iOOCtw3tgOPKEQ5hE

Using ternary conditional operator to get the type we want for a template type is often used in C++.

Here the same idea applies in C macro:

#define __is_constexpr(x) \
	(sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
Details:
 - sizeof() is an integer constant expression, and does not evaluate the
   value of its operand; it only examines the type of its operand.
 - The results of comparing two integer constant expressions is also
   an integer constant expression.
 - The use of literal "8" is to avoid warnings about unaligned pointers;
   these could otherwise just be "1"s.
 - (long)(x) is used to avoid warnings about 64-bit types on 32-bit
   architectures.
 - The C standard defines an "integer constant expression" as different
   from a "null pointer constant" (an integer constant 0 pointer).
 - The conditional operator ("... ? ... : ...") returns the type of the
   operand that isn't a null pointer constant. This behavior is the
   central mechanism of the macro.
 - If (x) is an integer constant expression, then the "* 0l" resolves it
   into a null pointer constant, which forces the conditional operator
   to return the type of the last operand: "(int *)".
 - If (x) is not an integer constant expression, then the type of the
   conditional operator is from the first operand: "(void *)".
 - sizeof(int) == 4 and sizeof(void) == 1.
 - The ultimate comparison to "sizeof(int)" chooses between either:
     sizeof(*((int *) (8)) == sizeof(int)   (x was a constant expression)
     sizeof(*((void *)(8)) == sizeof(void)  (x was not a constant expression)


For a conditional expression (?:) to be an lvalue, the second and third operands must be lvalues of the same type.
This is because the type and value category of a conditional expression is determined at compile time and must be appropriate whether or not the condition is true.
If one of the operands must be converted to a different type to match the other than the conditional expression cannot be an lvalue as the result of this conversion would not be an lvalue (but a r-value).

Thus:

OK:
int x = 1;
int y = 2;
(x > y ? x : y) = 100; // l-value on the left side of =

Not OK:
int x = 1;
long y = 2;
(x > y ? x : y) = 100; // type conversion to long as r-value type

Feb 7, 2022

[C++] can't we use compile-time 'variables' in consteval functions as template parameters?

Reference:
https://stackoverflow.com/a/70905771
P1306 : Expansion statements 
tracking: https://github.com/cplusplus/papers/issues/156



// Returns the nth type in a parameter pack of types (ommited for clarity)
//   template <std::size_t N, typename...Ts>
//   nth_type{}

template <typename... Ts>
struct Typelist{
    template <typename T>
    consteval static std::size_t pos() noexcept { 
        for(std::size_t i{}; i < sizeof...(Ts); ++i) {
            using TN = nth_type_t<i, Ts...>;
            if (std::is_same_v<T, TN>) 
                return i;
        }
        return sizeof...(Ts);
    }
};

  • Doesn't matter that i is guaranteed to be evaluated only at compile-time when its value is known in an abstract sense.
  • Doesn't matter whether the function is consteval or constexpr or none of these.
The language is still statically typed and nth_type_t; must in any given instantiation of the function refer to exactly one type. 
If i can change in the for loop, that is not possible to guarantee. The language requires that the expression i when used as template argument is by itself a constant expression, independently of whether the whole function body can only be evaluated as part of a larger constant expression.
But i is neither declared constexpr, nor declared const with constant initializer.

Oct 13, 2021

[C++] std::shared_mutex

Reference:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html#shared_mutex_imp
https://stackoverflow.com/a/57709957


shared_mutex can certainly be implemented on top of an OS supplied read-write mutex. However a portable subset of the implementation is shown here for the purpose of motivating the existence of cond_var: the razor-thin layer over the OS condition variable.

A secondary motivation is to explain the lack of reader-writer priority policies in shared_mutex.

This is due to an algorithm credited to Alexander Terekhov which lets the OS decide which thread is the next to get the lock without caring whether a unique lock or shared lock is being sought. This results in a complete lack of reader or writer starvation. It is simply fair.


GCC on linux underneath using pthread_rwlock_t for the std::shared_lock implementation, to use kernel’s scheduler, there’s a header config:
_GLIBCXX_USE_PTHREAD_RWLOCK_T
to disable pthread_rwlock_t.

for pthread_rwlock_t,  by default it prefers read than write.

It can be config through:
PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP

Feb 25, 2019

[Go] build tag constraint useful for conditional const variable

Reference:
Is there a way to define a constant at build time in Go?
Go Build Constraints

main.go
package main
import "fmt"

func main() {
    fmt.Println(f)
}

foo.go
// +build foo
package main
const f = "defined in foo.go"

bar.go
// +build bar
package main
const f = "defined in bar.go"

Compiling the code with different tags will give different results:
$ go build -tags foo
$ ./main
defined in foo.go
$ go build -tags bar
$ ./main
defined in bar.go

This trick also useful under 'go test'
$ go test -tags=foo

Feb 15, 2019

[Go] escape analysis discussion

func call(f func()) {
    f()
}

func g() {
    var x int
    
    call(
        func() { x = 1 },  // No escape since 'x' is not used by caller
    )
}

Originally, closures always stored addresses of referenced variables.

At some point an optimization was added that captured variables that are not later modified by the outer function have their values, not their addresses, recorded in the closure.
Please refer to this question on stackoverflow, which is quite interesting due to this side-effect with golang's closure behavior:
https://stackoverflow.com/questions/42162879/mutex-within-loop-leads-to-unexpected-output
package main

import (
    "fmt"
    "sync"
)

func main() {
    mutex := new(sync.Mutex)

    for i := 1; i < 5; i++ {
        for j := 1; j < 5; j++ {
            mutex.Lock()
            go func() {
                fmt.Printf("%d + %d = %d\n", i, j, j+i)
                mutex.Unlock()
            }()
        }
    }
}
---
Result:
1 + 2 = 3
1 + 3 = 4
1 + 4 = 5
2 + 5 = 7
2 + 2 = 4
2 + 3 = 5
2 + 4 = 6
3 + 5 = 8
3 + 2 = 5
3 + 3 = 6
3 + 4 = 7
4 + 5 = 9
4 + 2 = 6
4 + 3 = 7
4 + 4 = 8
---

If not for this optimization, the same problems that force heap allocation above would force heap allocation even for:
func call(f func() int) { 
    f()
}

func g() {
    var x int
    call(func() int { return x })
}

If we tweak that example to modify x after the closure creation, that will disable the closure-value optimization:
func call(f func() int) {
    f()
}

func g() {
    var x int
    call(func() int { return x })  // 'x' escaped to heap since 'x++' from the caller
    x++
}

if the closure itself escapes, then the addresses of the variables are understood to escape too:
func call(f func() *int) *int { 
    f()
    return nil
}

func h() { 
    var y int
    call(
    func() *int { return &y }
    )
}


Or even this:
func h() {
    var y int
    _ = func() *int { return &y }()
}


Both of them decide that &y escapes, and there isn't even a call to analyze in the second.

Golang currently treats values returned by functions (including closures) as escaping to the heap, so there's really no point in worrying that f() might return a value from within the closure.

package p

//go:noinline
func call1(f func() error) error {
 // Leaks *f to result.
 return f()
}

func F1() error {
 y := new(int)
 return call1(func() error {
  y = nil
  return nil
 })
}


//go:noinline
func call2(f func() error) error {
 // No param leakage.
 f()
 return nil
}

func F2() error {
 y := new(int)
 return call2(func() error {
  y = nil
  return nil
 })
}



There's a discussion about interface as function parameter type which causing variable passing in heap allocated.
Reference:
https://www.reddit.com/r/golang/comments/9f9pu8/do_blank_interface_values_escape_to_the_heap/
https://www.reddit.com/r/golang/comments/badeql/golang_memory_escape_analysis_is_naive/
https://stackoverflow.com/a/44699604

The above stackoverflow answer is incorrect, it's not the interface{} being allocated on the heap but the variable it's taken should be allocated on the heap which interface{}'s second pointer will points to it.
(The first pointer will point to type information, or the word will contains the type information, depends on the implement)

Nov 27, 2018

[C++][cppconf 2018] "Trivially Relocatable" Arthur O'Dwyer



Definition:
iff a move constructor and a destructor are non-trivial in pair,
the result is tantamount to 'memcpy', thus defined by Arthur as 'relocatable'.

e.g std::shared_ptr

Thus, in compiler, can't we just do a 'memcpy' instead of following
the language syntax doing a move construct + destruct?
Yes, we can!


Reference:
https://quuxplusone.github.io/blog/2018/07/18/announcing-trivially-relocatable/
https://www.youtube.com/watch?v=8u5Qi4FgTP8

assembly: call vs. callq
https://stackoverflow.com/a/46753525
quote:
It's just 'call'. Use Intel-syntax disassembly if you want to be able to look up instructions in the Intel/AMD manuals.

The q operand-size suffix does technically apply (it pushes a 64-bit return address and treats RIP as a 64-bit register), but there's no way to override it with instruction prefixes.
i.e. calll and callw aren't encodeable in 64-bit mode, so it's just annoying that some AT&T syntax tools show it as callq instead of call. This of course applies to retq as well.


Is this new?
nope :-)
In Lippman's Inside the C++ Object Model mentioned that inside the copy constructor
we could use system call memcpy to speed up the bit-wise copy; however, there's a gotcha, which is  object slice, which could accidentally copy the derived object's v-ptr.
In Arthur's proposal, we modify compiler(not run-time), implement the memcpy directly when
the type has Rule Of Zero trait.
C++ core quideline: C.20: If you can avoid defining default operations, do
C++ core guideline: C.67: A polymorphic class should suppress copying

Aug 21, 2018

[C++] new auto

reference:
https://en.cppreference.com/w/cpp/language/new
ISO 7.6.2.4.2
https://stackoverflow.com/questions/37924996/lambda-with-dynamic-storage-duration

#include <iostream>

using namespace std;


int main()
{
    // movie 'Se7en"
    auto fun = new auto([]() { cout << "oh he didn't know~~" << endl; });
    (*fun)();
}

Aug 12, 2018

[C++][clang][gcc] tail call optimization

#include <iostream>
using namespace std;

int voidret(int i)
{
    if (i < 0) {
        i++;
    }
    else {
        i--;
    }
    return voidret(i);
}


int main()
{
    auto i = voidret(10);
}

clang++ -O3 result:
2030951640

g++ -O3 result:
indifinite

clang++ -O3 assembly:
voidret(int):                            # @voidret(int)
        ret
main:                                   # @main
        xor     eax, eax
        ret
_GLOBAL__sub_I_example.cpp:             # @_GLOBAL__sub_I_example.cpp
        push    rax
        mov     edi, offset std::__ioinit
        call    std::ios_base::Init::Init() [complete object constructor]
        mov     edi, offset std::ios_base::Init::~Init() [complete object destructor]
        mov     esi, offset std::__ioinit
        mov     edx, offset __dso_handle
        pop     rax
        jmp     __cxa_atexit            # TAILCALL

g++ -O3 assembly:
voidret(int):
.L2:
        jmp     .L2
main:
        mov     edi, 10
        call    voidret(int)
_GLOBAL__sub_I_voidret(int):
        sub     rsp, 8
        mov     edi, OFFSET FLAT:_ZStL8__ioinit
        call    std::ios_base::Init::Init() [complete object constructor]
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:_ZStL8__ioinit
        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
        add     rsp, 8
        jmp     __cxa_atexit

Reasoning:
https://stackoverflow.com/questions/18478078/clang-infinite-tail-recursion-optimization

quote:
While both g++ and clang++ are able to compile C++98 and C++11 code, clang++ was designed from the start as a C++11 compiler and has some C++11 behaviors embedded in its DNA.

With C++11 the C++ standard became thread aware, and that means that now there are some specific thread behavior. In particular 6.8.2.2 states:
The implementation may assume that any thread will eventually do one of the following:
  • terminate,
  • make a call to a library I/O function,
  • perform an access through a volatile glvalue, or
  • perform a synchronization operation or an atomic operation.
[ Note: This is intended to allow compiler transformations such as removal of empty loops, even when termination cannot be proven. — end note ]

And that is precisely what clang++ is doing when optimizing. It sees that the function has no side effects and removes it even if it does not terminate.

Jun 21, 2018

[Go] empty select

select{} // this block forever
i.e In CSP-speak, the empty select is like STOP, a process that never proceeds. This is akin to a self-deadlock and I imagine it doesn't get used all that often. However, the question proposed an interesting and genuine use-case for this pattern. An empty for{} loop is a self-livelock, different because it consumes cpu resources.

May 28, 2018

[C++] is inherit const type valid?

Valid since while using typedef, i.e template<typename>
is really typedef, if the type alias is used as a type name, the c.v will be dropped.

[class.name]/5:
If a typedef-name that names a cv-qualified class type is used where a class-name is required, the cv-qualifiers are ignored.

struct base {
};

template<typename T>
struct inherit : T {
    using T::T;
};

int main() {
    inherit<base const>{};
}

[C++][C++17] shared_ptr now taking array type

boost::shared_array (deprecated) due to now(C++17) shared_ptr takes array type.

cppref:
std::shared_ptr::operator[]

Prior C++17, we need to provide ourselves a array deleter in order to allow shared_ptr taking array type.

s.t like:
template< typename T >
struct array_deleter
{
  void operator ()( T const * p)
  { 
    delete[] p; 
  }
};

std::shared_ptr<int> sp(new int[10], array_deleter<int>());

// or
std::shared_ptr<int> sp(new int[10], std::default_delete<int>());
//or
std::shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; });
Reference:
https://stackoverflow.com/a/13062069

May 20, 2018

[C++][compiler][UB] Undefined Behavior and Compiler Optimizations


C++Now 2018: John Regehr “Closing Keynote: Undefined Behavior and Compiler Optimizations”

Note:

  • Inside function stack, make sure always, always init auto variable.
  • UB facilitates optimizations by allowing safety checks to be decoupled from unsafe operations.
  • All-or-nothing semantics for UB is not appropriate in a compiler IR.
  • Compiler must never make code less defined.
  • Deferred UB can work.
    • A tool we can use to justify desirable compiler optimizations
  • Flat memory model is easy to reason.
  • We need two types of pointers:
    • logical pointer - originate at allocations
      i.e malloc/new
      It use dataflow-based provenance.
      If originating at different allocations never alias.
      i.e pointer points to the location that is pass the complete object and compare that location to another complete object is unspecified.
    • Physical pointers - originate at casts from integer to pointer
Reference:
Pointers are literal types. They can be constexpr under certain conditions:

[expr.const]
... [a pointer is constexpr if] it contains the address of an object with static storage duration, the address past the end of such an object (5.7), the address of a function, or a null pointer value.
  1. static storage duration
  2. the address past the end of such an object 
  3. the address of a function
  4. null pointer value
int x;

int main()
{
    constexpr int *ptr = &x; // Compiles.

    // Doesn't compile: `error: '& foo' is not a constant expression`
    // int foo;
    // constexpr int *bar = &foo;
}

  • Fun consequences of control-flow-based pointer provenance:
    • assume p is a logical pointer
    • p and (int *)(int)p are not necessarily the same...
LLVM wants to optimize (int *)(int)p to p
WRONG! Provenance information can be lost.
Both LLVM/GCC can miscompile code like above...

  • It's only valid to compare pointeres with overlapping liveness
    • Potentially illegal to trim liveness ranges
  • Moving allocations around should be careful!

char *p = malloc(4);
char *q = malloc(4);

//valid
if (p == q) {...;}

free(p);
char *p = malloc(4);
free(p);

char *q = malloc(4);

// UB
if (p == q) {...}