PrevUpHomeNext

Home:: tiv.cc

CPP20 Quick Look


CPP20 (c++20) Quick Look: from cpp17 to cpp20

CPP20 (c++20) Quick Look: from c++17 to c++20





cppreference c++20: https://en.cppreference.com/w/cpp/20

Use utxcpp, utxcpp Docs: https://cppfx.xyz/fx/static/docs/utxcpp/html





Concepts

________________________________________________________________________

What is c++ concepts

The concepts library provides definitions of fundamental library concepts that can be used to perform compile-time validation of template arguments and perform function dispatch based on properties of types. These concepts provide a foundation for equational reasoning in programs.
https://en.cppreference.com/w/cpp/concepts

Write a concept

Define a simplest concept

template <typename type>
concept type_pass  = true;

Define a little more complicated concept

template <typename type>
class is_boolean
{
public:
	constexpr static bool value = false;
};

template <>
class is_boolean<bool>
{
public:
	constexpr static bool value = true;
};

template <typename type>
constexpr bool is_boolean_v = is_boolean<type>::value;

template <typename type>
concept boolean = is_boolean_v<type>;

Use the concepts

template <type_pass type>
class test1
{
};

template <boolean type>
class test2
{
};

int main()
{
// type_pass concept accepts any type.
	test1<bool>{}; // OK
	test1<utx::i32>{}; // OK
	
// boolean concept only accepts bool type.
	test2<bool>{}; // OK
	//test2<utx::i32>{}; // Failure
	//test2<int>{}; // Failure
	
	type_pass auto x = 123; // int
	type_pass auto y = 3.25; // double
	
	boolean auto z = true; // bool
	
	// boolean auto w = 9; // error: deduced initializer does not satisfy placeholder constraints
}

std concepts defined in standard

std::integral
std::floating_point
std::ranges::range
std::same_as
std::derived_from
std::convertible_to
std::copyable

And more:

https://en.cppreference.com/w/cpp/concepts

Constraints

A constraint is a sequence of logical operations and operands that specifies requirements on template arguments. They can appear within requires expressions or directly as bodies of concepts.

Conjunctions

The conjunction of two constraits is formed by using && in expression.

template <typename type>
concept integral_vector = std::integral<typename type::value_type>
	&& std::same_as<type, std::vector<typename type::value_type>>;

disjunctions

The disjunction of two constraits is formed by using || in expression.

template <typename type>
concept real_number = std::integral<type> || std::floating_point<type>;

https://en.cppreference.com/w/cpp/language/constraints

Requires Clauses

// OK
template <typename type>
concept concept_1 = requires (const type & val)
{
	{val.value} -> std::convertible_to<float>;
};
// OK
template <typename type>
concept concept_2 = requires {requires std::integral<type>;};
// OK
template <typename type>
	requires std::integral<type>
class test
{
};
// ERROR
template <typename type>
concept concept_3 = requires std::integral<type>;
// OK
template <typename type>
concept concept_4 = requires (const type & val)
{
	{val.value} -> std::same_as<double>;
	val.begin(); // expression + ;
	*val.begin(); // expression + ;
	requires std::integral<typename type::value_type>;
};

Top





Ranges

________________________________________________________________________

std::ranges::for_each
#include <iostream>
#include <vector>
#include <algorithm> // std::ranges::for_each
#include <boost/lambda/lambda.hpp>

namespace ld = boost::lambda;
using ld::_1;

int main()
{
	std::vector<int> vector{2,3,4,5};
	std::ranges::for_each(vector, std::cout << _1 << ' ');
	std::cout << '\n';
}
std::views::transform, std::views::filter
#include <ranges> // std::views::transform, std::views::filter
#include <vector>
#include <boost/lambda/lambda.hpp>
#include <algorithm> // std::ranges::for_each
#include <numeric> // std::iota
#include <utxcpp/core.hpp>

namespace ld = boost::lambda;
using ld::_1;

int main()
{
	std::vector<utx::i32> vector(50);
	std::iota(vector.begin(), vector.end(), 1);
	auto v1 = vector;
	auto v11 = v1 | std::views::transform(_1*_1) | std::views::filter(_1%2==0);
	std::ranges::for_each(v11, std::cout << _1 << ' ');
	std::cout << std::endl;
}
std::views::iota, std::ranges::copy
#include <boost/lambda/lambda.hpp>
namespace ld = boost::lambda;
using ld::_1;
using ld::_2;
using ld::_3;
#include <utxcpp/core.hpp>
#include <ranges> // std::views::iota
#include <algorithm> // std::ranges::copy
#include <vector>

int main()
{
	auto v1 = std::views::iota(1,16) | std::views::filter(_1%2==1) | std::views::transform(_1*1.3f);
	std::vector<utx::f32> v2(std::ranges::distance(v1));
	std::ranges::copy(v1, v2.begin());
	utx::print_all(v2);
/*
output:
1.300000 3.900000 6.500000 9.099999 11.700000 14.299999 16.900000 19.500000
*/
}
std::ranges::in_out_result
#include <boost/lambda/lambda.hpp>
namespace ld = boost::lambda;
using ld::_1;
using ld::_2;
using ld::_3;
#include <ranges>
#include <vector>
#include <utxcpp/core.hpp>
#include <algorithm> // std::ranges::copy, std::ranges::in_out_result

int main()
{
	auto v1 = std::views::iota(1,33) | std::views::filter(_1%3==2);
	std::vector<utx::i32> v2(32);
	std::ranges::in_out_result last = std::ranges::copy(v1, v2.begin());
	std::size_t in_size = std::ranges::distance(v1.begin(), last.in);
	std::size_t out_size = std::ranges::distance(v2.begin(), last.out);
	utx::print("in_size:", in_size, "out_size:", out_size);
	
	v2.resize(out_size);
	utx::print("v2:");
	utx::print_all(v2);
/*
Program Output:
in_size: 11 out_size: 11
v2:
2 5 8 11 14 17 20 23 26 29 32
*/
}
std::ranges::transform
#include <boost/lambda/lambda.hpp>
namespace ld = boost::lambda;
using ld::_1;
using ld::_2;
using ld::_3;
#include <vector>
#include <utxcpp/core.hpp>
#include <algorithm> // std::ranges::in_out_result

int main()
{
	std::vector<utx::i32> out(11);
	auto input = std::views::iota(1, (utx::i32)out.size()+1);
	std::ranges::in_out_result last = std::ranges::transform(input, out.begin(), _1*_1);
	
	std::ranges::for_each(input.begin(), last.in, std::cout << _1 << ' ');
	std::cout << '\n';
	
	std::ranges::for_each(out.begin(), last.out, std::cout << _1 << ' ');
	std::cout << '\n';
/*
Program Output:
1 2 3 4 5 6 7 8 9 10 11
1 4 9 16 25 36 49 64 81 100 121
*/
}

Top





Modules

________________________________________________________________________

* How to build c++20 modules with gcc

1. File: fizz_box.cpp

export module fizz_box;

export namespace fb
{
	template <typename T>
	class fizz_box
	{
	private:
		T x{}, y{};
	public:
		virtual ~fizz_box() = default;
		fizz_box() = default;
		fizz_box(const T & x, const T & y):
			x{x}, y{y}
		{}
		T sum() const {return x+y;}
	};
}

2. File: main.cpp

import fizz_box;

#include <iostream>

int main()
{
	fb::fizz_box<float> box;
	fb::fizz_box box2{1.28, 3.44};
	std::cout << box2.sum() << '\n'; // 4.72
}

3. Compile fizz_box.cpp

g++ fizz_box.cpp -c -fmodules-ts -std=c++20

4. Compile main.cpp

g++ main.cpp -c -fmodules-ts -std=c++20

5. Link fizz_box.o and main.o

g++ fizz_box.o main.o -o main
How to put include headers in a module
  1. Add a module declaration "module;" at the top of module source code
  2. Put include headers between "module;" and "export module fizz_box;"

File: fizz_box.cpp

module;

#include <iostream>

export module fizz_box;

export namespace fb
{
	template <typename T>
	class fizz_box
	{
	private:
		T value;
	public:
		virtual ~fizz_box() = default;
		fizz_box() = delete;
		fizz_box(const T & value):
			value{value}
		{
		}
		void print() const
		{
			std::cout << "value: " << value << '\n';
		}
	};
}

File: main.cpp

import fizz_box;

int main()
{
	fb::fizz_box<double> box{1.234};
	box.print();
}

Top





std::jthread: std::stop_token

________________________________________________________________________

std::jthread

std::stop_token

std::jthread::request_stop - Issues a stop request to the internal stop-state, if it has not yet already had stop requested.

std::stop_token::stop_requested - Checks if the stop_token object has associated stop-state and that state has received a stop request.

Example

#include <utxcpp/core.hpp> // utx::print
#include <utxcpp/thread.hpp> // utx::sleep
#include <thread>
#include <chrono>

auto worker = [] (std::stop_token token) -> void
{
	utx::umax value{1};
	while (true)
	{
		utx::printnl(value++, ' ');
		if (value%10 == 1)
			utx::print(); // print '\n'

		if (token.stop_requested())
			return;

		utx::sleep(0.1); // sleep 0.1 second
	}
};

int main()
{
	std::jthread worker_thread{worker};
	
	utx::sleep(7.456); // sleep 7.456 seconds
	worker_thread.request_stop();
	
	worker_thread.join();
	
	utx::print(); // print '\n'
	utx::sleep(1);
}

Top





std::jthread: std::stop_callback

________________________________________________________________________

#include <utxcpp/core.hpp>
#include <utxcpp/thread.hpp>
#include <thread>

int main()
{
	auto worker = std::jthread{
		[] (std::stop_token token)
		{
			utx::u64 counter{};
			while (true)
			{
				utx::print(counter++);
				if (token.stop_requested())
				{
					utx::print("stop requested!");
					return;
				}
				utx::sleep(0.24);
			}
		}
	};
	
	auto starter = std::jthread{
		[&worker] {
			utx::sleep(3.5);
			worker.request_stop();
		}
	};
	
	auto stop_callback = std::stop_callback{
		worker.get_stop_token(),
		[] {
			utx::print("std::stop_callback is called.");
		}
	};
	
	worker.join();
	starter.join();
/*
Output:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
std::stop_callback is called.
15
stop requested!
*/
}

Top





std::stop_token and cv

std::jthread: std::stop_token and condition variable

________________________________________________________________________

std::stop_token and std::condition_variable_any

#include <utxcpp/core.hpp>
#include <utxcpp/thread.hpp>
#include <thread>

std::mutex mutex;

auto worker = [] (std::stop_source source)
{
	std::condition_variable_any cv;
	std::stop_token token = source.get_token();

	std::unique_lock<std::mutex> lock{mutex};
	utx::print("worker");

	cv.wait(lock, token, [&token] {return token.stop_requested();});

	utx::print("stop requested");
};

auto starter = [] (std::stop_source source)
{
	utx::sleep(2.22);
	source.request_stop();
};

int main()
{
	std::stop_source source;
	auto worker_thread = std::jthread{worker, source};
	auto starter_thread = std::jthread{starter, source};
	worker_thread.join();
	starter_thread.join();
}

Top





std::jthread: std::stop_source

________________________________________________________________________

#include <utxcpp/core.hpp>
#include <utxcpp/thread.hpp>
#include <thread>
#include <vector>

std::mutex mutex;

auto worker = [] (utx::i16 id, std::stop_source source)
{
	std::stop_token token = source.get_token();
	while (true)
	{
		std::unique_lock<std::mutex> lock{mutex};
		utx::printnl(id, "");
		lock.unlock();
		if (token.stop_requested())
			return;
		utx::sleep(0.25);
	}
};

auto starter = [] (std::stop_source source)
{
	utx::sleep(2.975);
	source.request_stop();
};

void print_source(const std::stop_source & source)
{
	std::unique_lock<std::mutex> lock{mutex};
	utx::print("\nstop possible:", source.stop_possible(), "stop requested:", source.stop_requested());
}

int main()
{
	std::stop_source source;
	print_source(source);
	
	std::vector<std::jthread> pool;
	for (utx::i16 i=0; i<5; i++)
		pool.push_back(std::jthread{worker, i, source});
	
	pool.push_back(std::jthread{starter, source});
	
	print_source(source);
	
	for (auto & thread: pool)
		thread.join();
		
	print_source(source);
	print_source(source);
	
	utx::print(); // print '\n'
}


/*
Output:

stop possible: true stop requested: false
0 1 2 3 4
stop possible: true stop requested: false
0 1 3 2 4 0 1 2 4 3 0 1 2 4 3 0 2 1 4 3 0 2 1 4 3 0 2 3 1 4 0 2 1 4 3 0 2 4 1 3 0 2 4 3 1 0 4 3 2 1 0 4 3 2 1 0 4 3 2 1
stop possible: true stop requested: true

stop possible: true stop requested: true

*/

Top





std::atomic std::shared_ptr

std::atomic std::shared_ptr, std::weak_ptr

________________________________________________________________________

std::weak_ptr does not increase the use_count() of both std::weak_ptr and std::shared_ptr;

But std::shared_ptr increases the use_count() of both std::weak_ptr and std::shared_ptr.

#include <atomic>
#include <memory>
#include <utxcpp/core.hpp>

class fizz_box
{
public:
	fizz_box()
	{
		utx::print("created");
	}
	virtual ~fizz_box()
	{
		utx::print("removed");
	}
	void print() const
	{
		utx::print("Fizz Box");
	}
};

int main()
{
	using atomic_box = std::atomic<std::shared_ptr<fizz_box>>;
	{
		atomic_box box1;
		utx::print("box1 use_count:", box1.load().use_count()); // 0
		box1 = std::make_shared<fizz_box>();
		utx::print("box1 use_count:", box1.load().use_count()); // 2
		box1.load()->print();
		
		atomic_box box2 = std::make_shared<fizz_box>();
		
		atomic_box box3;
		box3.store(std::make_shared<fizz_box>());
	}
	{
		atomic_box box4 = std::make_shared<fizz_box>();
		utx::print("box4 use_count:", box4.load().use_count()); // 2
		{
			atomic_box _ = box4.load();
			utx::print("box4 use_count:", box4.load().use_count()); // 3
			{
				atomic_box __ = _.load();
				utx::print("box4 use_count:", box4.load().use_count()); // 4
			}
		}
		utx::print("box4 use_count:", box4.load().use_count()); // 2
	}
	
	using weak_box = std::atomic<std::weak_ptr<fizz_box>>;
	{
		weak_box box5;
		utx::print("box5 use_count:", box5.load().use_count()); // 0
		
		atomic_box box6 = std::make_shared<fizz_box>();
		box5 = box6.load();
		utx::print("box5 use_count:", box5.load().use_count()); // 1
		utx::print("box6 use_count:", box6.load().use_count()); // 2
		
		atomic_box __ = box6.load();
		utx::print("box5 use_count:", box5.load().use_count()); // 2
		utx::print("box6 use_count:", box6.load().use_count()); // 3
		
		//weak_box ___ = __.load(); // error
		weak_box ___;
		___.store(__.load()); // OK
		utx::print("box5 use_count:", box5.load().use_count()); // 2
		utx::print("box6 use_count:", box6.load().use_count()); // 3
		
		weak_box ____;
		____.store(box5.load());
		utx::print("box5 use_count:", box5.load().use_count()); // 2
		utx::print("box6 use_count:", box6.load().use_count()); // 3
	}
}

Top





Designated Initializers

________________________________________________________________________

Designated Initializers must be used for aggregate type.

What is aggregate type:
https://en.cppreference.com/w/cpp/language/aggregate_initialization

#include <utxcpp/core.hpp>

class fizz_box
{
public:
	double x;
	std::string y;
	std::complex<double> z;
	int w;
public:
	// Let it be printable by utxcpp.
	friend auto & operator<<(std::ostream & out, const fizz_box & box)
	{
		return out << box.x << ' ' << box.y << ' ' << box.z << ' ' << box.w;
	}
};

int main()
{
	// Designated Initializer
	fizz_box fb1{.x=2.3, .y="fizz box", .z={1.2,2.3}, .w=12}; // OK
	utx::print(fb1);
	
	// Designated Initializer
	fizz_box fb2{.y="box", .w=123}; // OK
	utx::print(fb2);
	
	//fizz_box fb3{.w=321, .x=3.21}; // error: designator order does not match.
}





Three-Way Comparison

________________________________________________________________________

Three-Way Comparison

3-way comparison (or spaceship operator)

#include <iostream>
#include <compare> // std::strong_ordering

int main()
{
	int x = 123;
	int y = 321;
	auto result = x<=>y;
	static_assert(std::same_as<decltype(result), std::strong_ordering>);
	std::cout << (result<0?"<":result>0?">":"==") << std::endl;
}
Default Comparator

To let two objects comparable, you have to define many operators:

operator<
operator>
operator==
operator!=
operator<=
operator>=

However, with spaceship operator: <=>, you just need to declare one default:

operator<=>

Example:

#include <iostream>
#include <string>
#include <string_view>

class fizz_t
{
private:
	std::string str;
	int x;
public:
	fizz_t(const std::string_view str, const int & x):
		str{str}, x{x}
	{}
	fizz_t() = default;
};

class buzz_t
{
private:
	std::string str;
	int x;
public:
	buzz_t(const std::string_view str, const int & x):
		str{str}, x{x}
	{}
	buzz_t() = default;
	auto operator<=>(const buzz_t &) const  = default;
};

int main()
{
	fizz_t f1{"abc", 9}, f2{"abc", 7};
	//bool result = f1 > f2; // error
	
	buzz_t b1{"abc", 9}, b2{"abc", 7};
	bool result = b1 > b2; // OK
	
	std::cout << std::boolalpha << result << '\n'; // true
}

Top





Map Contains

________________________________________________________________________

std::map has an contains method in c++20 now.

Example

#include <iostream>
#include <map>
#include <string>

using namespace std::string_literals;

int main()
{
	std::map<std::string, std::int32_t> map {
		{"size"s, 123},
		{"type"s, 1},
		{"shape"s, 9}
	};
	
	if (map.contains("shape"s))
	{
		std::cout << map["shape"s] << std::endl; // 9
	}
}

Top





Range-Based-For Initialization

________________________________________________________________________

Range-Base For Loop with Initialization

Example

#include <iostream>
#include <vector>

int main()
{
	for (std::vector vector{1,2,3,4,5,6,7,8,9}; int & x: vector)
	{
		static std::size_t counter{};
		std::cout << x << (++counter==vector.size()?'\n':' ');
	}
}

Top





std::numbers

________________________________________________________________________

All numbers are constexpr.

All numbers are floating point: std::floating_point.

Example

#include <numbers>
#include <utxcpp/core.hpp>

namespace nb = std::numbers;

int main()
{
// All numbers are constexpr
// All numbers are floating point: std::floating_point
	utx::print(
		nb::pi,
		nb::e,
		nb::phi,
		nb::egamma,
		'\n',
		nb::pi_v<utx::fmax>,
		nb::e_v<utx::fmax>,
		nb::phi_v<utx::fmax>,
		nb::egamma_v<utx::fmax>,
		'\n',
		nb::inv_pi,
		nb::inv_sqrtpi,
		'\n',
		nb::ln2,
		nb::ln10,
		nb::log2e,
		nb::log10e,
		'\n',
		nb::sqrt2,
		nb::sqrt3,
		nb::inv_sqrt3
	);
}

Top





std::span

________________________________________________________________________

std::span is a reference to another conteguous STL container.

#include <utxcpp/core.hpp>
#include <span>
#include <vector>
#include <algorithm>
#include <boost/lambda/lambda.hpp>
#include <numeric>

namespace ld = boost::lambda;
using ld::_1;

int main()
{
	auto vector = std::vector<int>{5,4,3,2,1};
	auto span = std::span<int>{vector};
	utx::print_all(span); //5 4 3 2 1
	
	std::transform(vector.begin(), vector.end(), vector.begin(), _1*_1);
	utx::print_all(span); //25 16 9 4 1
	
	std::iota(vector.begin(), vector.end(), -10);
	utx::print_all(span); //-10 -9 -8 -7 -6
}

Top





std::source_location

________________________________________________________________________

std::source_location::current

std::source_location::file_name

std::source_location::function_name

std::source_location::line

std::source_location::column

Example

#include <utxcpp/core.hpp>
#include <source_location>

class fizz_box
{
public:
	fizz_box()
	{
		std::source_location location = std::source_location::current();
		utx::print(
			"Constructor,", location.file_name(), location.line(), location.column(), location.function_name()
		);
	}
	virtual ~fizz_box()
	{
		std::source_location location = std::source_location::current();
		utx::print(
			"Destructor,", location.file_name(), location.line(), location.column(), location.function_name()
		);
	}
};

void test(std::source_location location = std::source_location::current())
{
	utx::print(
		location.file_name(), location.function_name(), location.line(), location.column()
	);
}

inline void fn()
{
	test();
}

int main()
{
	fizz_box{};
	fn();
	test();
}

Output:

Constructor, fizz_box.cpp 9 64 fizz_box::fizz_box()
Destructor, fizz_boxx.cpp 16 64 virtual fizz_box::~fizz_box()
fizz_box.cpp void fn() 32 6
fizz_box.cpp int main() 39 6

Top





std::endian

________________________________________________________________________

enum class std::endian

Example

#include <bit> // std::endian
#include <utxcpp/core.hpp>
#include <string>

std::string endian()
{
	switch (std::endian::native)
	{
	case std::endian::big:
		return "std::endian::big";
	case std::endian::little:
		return "std::endian::little";
	default:
		return "mixed";
	}
}

int main()
{
	utx::print(endian());
}

Top





std::bit_cast

________________________________________________________________________

template <class To, class From>
constexpr To bit_cast(const From & from) noexcept;

Every bit in the value representation of the returned To object is equal to the corresponding bit in the object representation of from.
https://en.cppreference.com/w/cpp/numeric/bit_cast

When it is needed to interpret the bytes of an object as a value of a different type, std::bit_cast (since C++20) can be used.
https://en.cppreference.com/w/cpp/language/reinterpret_cast

According to cppreference, std::bit_cast is more safe than reinterpret_cast .

// https://en.cppreference.com/w/cpp/numeric/bit_cast
// https://en.cppreference.com/w/cpp/language/reinterpret_cast

#include <utxcpp/core.hpp>
#include <bit> // std::bit_cast

int main()
try
{
	static_assert(sizeof (double) == sizeof (std::int64_t));
	
	double x = 0.1;
	
	// reinterpret_cast can not cast constexpr value, and this is undefined behavior.
	[[maybe_unused]] std::int64_t y1 = * reinterpret_cast<std::int64_t *>(&x); // Undefined behavior.
	
	std::int64_t y2 = std::bit_cast<std::int64_t>(x); // OK
	
	double z = std::bit_cast<double>(y2); // OK
	utx::rt_assert(x == z);
	utx::print(x, y2, z);
}
catch (std::exception & e)
{
	utx::printe("std::exception=>", e.what());
}

Top





std::byteswap

________________________________________________________________________

Reverse value by bytes.

Example

#include <utxcpp/core.hpp>
#include <bit>

int main()
{
	constexpr utx::u32 x = 0x12345678;
	constexpr utx::u32 y = 0x78563412;
	static_assert(std::byteswap(x) == y);
	static_assert(std::byteswap(y) == x);
}

Top





std::popcount

________________________________________________________________________

template <class T>
constexpr int popcount(T x) noexcept;

Count how many 1-bits in x.

T must be unsigned integral.

Example

#include <utxcpp/core.hpp>
#include <bit> // std::popcount

int main()
{
	for (utx::u32 x: {0b10110101, 0b00110110})
	{
		utx::print(std::popcount(x));
	}
/*
5
4
*/
}

Top





constexpr, consteval, constinit

________________________________________________________________________

constexpr

The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time.
https://en.cppreference.com/w/cpp/language/constexpr

#include <utxcpp/core.hpp>

template <auto fn>
class func
{
public:
	func()
	{
		fn();
	}
};

constexpr auto fn = [] {utx::print("Hello, c++!");};

int main()
{
	func<fn>{};
	constexpr auto fn2 = [] {utx::print("Hello, boost!");};
	func<fn2>{};
}
consteval

The consteval specifier declares a function or function template to be an immediate function, that is, every potentially-evaluated call to the function must (directly or indirectly) produce a compile time constant expression.
https://en.cppreference.com/w/cpp/language/consteval

#include <utxcpp/core.hpp>
#include <type_traits>

template <typename T>
consteval auto fn()
{
	//std::cout << "Hello, 123" << std::endl; // error: std::cout is not constexpr
	if (std::is_constant_evaluated())
		return 123;
	return 100;
}

consteval auto func()
{
	if (std::is_constant_evaluated())
		return 324;
	return 200;
}

int main()
try
{
	auto x = fn<utx::f32>() + func();
	utx::rt_assert(x != 300);
	utx::rt_assert(x == 447);
	utx::print("Bye!");
}
catch (std::exception & e)
{
	utx::printe(e.what());
}
constinit

The constinit specifier declares a variable with static or thread storage duration. If a variable is declared with constinit, its initializing declaration must be applied with constinit. If a variable declared with constinit has dynamic initialization, the program is ill-formed. If no constinit declaration is reachable at the point of the initializing declaration, the program is ill-formed, no diagnostic required. constinit cannot be used together with constexpr or consteval.
https://en.cppreference.com/w/cpp/language/constinit

#include <utxcpp/core.hpp>
#include <type_traits>

constinit auto fn = []
{
	if (std::is_constant_evaluated())
		return 123;
	else
		return 321;
};

constinit auto value1 = 9;

int main()
{
	constexpr auto x = fn();
	auto y = fn();
	utx::print(x, y); //123 321
	
	//constinit auto value2 = 7;
	//error: constinit can only be applied to a variable with static or thread storage duration.
}

Top





std::is_constant_evaluated

________________________________________________________________________

Example

#include <utxcpp/core.hpp>
#include <type_traits> // std::is_constant_evaluated

constexpr auto fn()
{
	if (std::is_constant_evaluated())
		return true;
	else
		return false;
}

int main()
{
	auto a = fn();
	constexpr auto b = fn();
	utx::print(a, b); //false true
}

Top





std::remove_cvref

________________________________________________________________________

std::remove_cvref, std::decay, decltype, std::declval

Example

#include <utxcpp/core.hpp>

int main()
{
////////////////////////////////////////////////////////////////////////
	int t;
	const volatile int & x = t;
	utx::same_assert<std::remove_cvref_t<decltype(x)>, int, true>{};
	utx::same_assert<decltype(x), int, false>{};
////////////////////////////////////////////////////////////////////////
	const int y[] = {2,3,4};
	utx::same_assert<std::decay_t<decltype(y)>, const int *, true>{};
////////////////////////////////////////////////////////////////////////
	class fizz_box
	{
	public:
		fizz_box() = delete;
		float get() {return 1.0f;}
	};
	
	utx::same_assert<
		decltype(
			std::declval<fizz_box>().get()
		),
		float,
		true
	>{};
}

Top





std::assume_aligned

________________________________________________________________________

template <std::size_t N, class T>
[[nodiscard]] constexpr T * assume_aligned(T * ptr);

Informs the implementation that the object ptr points to is aligned to at least N. The implementation may use this information to generate more efficient code, but it might only make this assumption if the object is accessed via the return value of assume_aligned.

The program is ill-formed if N is not a power of 2. The behavior is undefined if ptr does not point to an object of type T (ignoring cv-qualification at every level), or if the object's alignment is not at least N.

https://en.cppreference.com/w/cpp/memory/assume_aligned

#include <utxcpp/core.hpp>
#include <memory> // std::assume_aligned

class fizz_box
{
private:
	int x{};
	float y{};
	double z{};
public:
	fizz_box(int x, float y, double z):
		x{x}, y{y}, z{z}
	{
		utx::print("created");
	}
	virtual ~fizz_box()
	{
		utx::print("removed");
	}
	void print() const
	{
		utx::print(x, y, z);
	}
};

int main()
{
	fizz_box * ptr = std::assume_aligned<64>(new fizz_box{123, 1.23f, 12.3});
	ptr->print();
	delete ptr;
}

/*
Output:
created
123 1.230000 12.300000
removed
*/

Top





std::string starts_with, ends_with

________________________________________________________________________

Example

#include <utxcpp/core.hpp>
#include <string>

int main()
{
	std::string str = "Hello, World!";
	utx::print(str.starts_with("Hell"), str.ends_with('!')); //true true
}

Top





Safe Integral Comparisons

________________________________________________________________________

Safe Integral Comparisions:

template <typename T, typename U>
constexpr bool cmp_???(T t, U u) noexcept;

T and U must be std::integral.

Compare the values of two integers t and u. Unlike builtin comparison operators, negative signed integers always compare less than (and not equal to) unsigned integers: the comparison is safe against lossy integer conversion.
https://en.cppreference.com/w/cpp/utility/intcmp

They are more safe than builtin integral comparison operators.

Example

#include <utxcpp/core.hpp>
#include <utility> // std::cmp_???

int main()
try
{
	constexpr std::uint32_t x = 0;
	constexpr std::int32_t y = -1;
	static_assert(! std::cmp_greater(y, x)); // OK
	static_assert(y > x); // Compiling pass, builtin comparison operator
}
catch (std::exception & e)
{
	utx::printe(e.what());
}

Top





std::type_identity

________________________________________________________________________

Example

#include <utxcpp/core.hpp>
#include <type_traits> // std::type_identity

template <typename T>
void test(const T & x, const std::type_identity_t<T> & y)
{
	utx::print(x + y);
}

int main()
{
	test(1.23, 2); //3.230000
	test(2, 1.23); //3
}

Top





std::to_address

________________________________________________________________________

std::to_address: converts a pointer to a raw pointer.

Example

#include <utxcpp/core.hpp>
#include <memory>

int main()
{
	auto ptr = std::make_shared<int>(123);
	auto ptr2 = std::to_address(ptr);
	utx::same_assert<decltype(ptr2), int *, true>{};
	utx::same_assert<decltype(ptr), int *, false>{};
	
	auto ptr3 = std::make_unique<int>(321);
	auto ptr4 = std::to_address(ptr3);
	utx::same_assert<decltype(ptr4), int *, true>{};
	utx::same_assert<decltype(ptr3), int *, false>{};
}

Top





std::memory_order be scoped

________________________________________________________________________

std::memory_order becomes scoped enumeration in c++20.

enum class memory_order : /*unspecified*/ {
	relaxed, consume, acquire, release, acq_rel, seq_cst
};

inline constexpr memory_order memory_order_relaxed = memory_order::relaxed;
inline constexpr memory_order memory_order_consume = memory_order::consume;
inline constexpr memory_order memory_order_acquire = memory_order::acquire;
inline constexpr memory_order memory_order_release = memory_order::release;
inline constexpr memory_order memory_order_acq_rel = memory_order::acq_rel;
inline constexpr memory_order memory_order_seq_cst = memory_order::seq_cst;

Example

#include <utxcpp/core.hpp>
#include <atomic> // std::memory_order

int main()
{
	std::atomic<int> x;
	x.store(123, std::memory_order_seq_cst);
	utx::print(x.load(std::memory_order_seq_cst));
}

Top





Likely and Unlikely

________________________________________________________________________

Likely and Unlikely attributes:

[[likely]]
[[unlikely]]

Allow the compiler to optimize for the case where paths of execution including that statement are more or less likely than any alternative path of execution that does not include such a statement.
https://en.cppreference.com/w/cpp/language/attributes/likely

#include <iostream>

int main()
{
	bool condition = true;
	
// if
	[[likely]]
	if (condition)
		std::cout << "yes" << std::endl;
	
// while loop
	[[unlikely]]
	while (!condition)
	{
		std::cout << "go\n";
		condition = true;
	}
	
// switch
	int x{};
	switch (x)
	{
	case 2:
		[[fallthrough]];
	case 1:
		std::cout << "+\n";
		break;
	
	[[likely]]
	case 0:
		std::cout << "0\n";
		break;
	
	[[unlikely]]
	case -1:
		break;
	default:
		break;
	}
}

Top





Calendar and Timezone

________________________________________________________________________

Need Compiler Support.

Calendar:

https://en.cppreference.com/w/cpp/chrono#Calendar

Timezone:

https://en.cppreference.com/w/cpp/chrono#Time_zone

Top





Bounded and Unbounded Array

________________________________________________________________________

std::is_bounded_array_v

std::is_unbounded_array_v

Example

#include <iostream>
#include <vector>
#include <boost/type_index.hpp>
#include <string>
#include <array>

template <typename type>
class test
{
public:
	test(type value = {})
	{
		const std::string name = boost::typeindex::type_id<type>().pretty_name();
		
		std::cout << "std::is_bounded_array_v<" << name << "> : "
			<< std::is_bounded_array_v<type> << '\n';
		std::cout << "std::is_unbounded_array_v<" << name << "> : "
			<< std::is_unbounded_array_v<type> << '\n';
		std::cout << std::endl;
	}
};

int main()
{
	test<int>{};
	test<std::array<int, 10>>{};
	test<std::vector<float>>{};
	test<int [10]>{};
	test<int []>{};
	int * ptr = new int[10];
	test{ptr};
	delete [] ptr;
	int * ptr2 = new int{};
	test{ptr2};
	delete ptr2;
	test<std::string>{};
	test<std::string>{};
	test<const char *>{};
}

Output:

std::is_bounded_array_v<int> : false
std::is_unbounded_array_v<int> : false

std::is_bounded_array_v<std::array<int, 10ul>> : false
std::is_unbounded_array_v<std::array<int, 10ul>> : false

std::is_bounded_array_v<std::vector<float, std::allocator<float> >> : false
std::is_unbounded_array_v<std::vector<float, std::allocator<float> >> : false

std::is_bounded_array_v<int [10]> : true
std::is_unbounded_array_v<int [10]> : false

std::is_bounded_array_v<int []> : false
std::is_unbounded_array_v<int []> : true

std::is_bounded_array_v<int*> : false
std::is_unbounded_array_v<int*> : false

std::is_bounded_array_v<int*> : false
std::is_unbounded_array_v<int*> : false

std::is_bounded_array_v<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >> : false
std::is_unbounded_array_v<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >> : false

std::is_bounded_array_v<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >> : false
std::is_unbounded_array_v<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >> : false

std::is_bounded_array_v<char const*> : false
std::is_unbounded_array_v<char const*> : false

Top





std::to_array

________________________________________________________________________

std::to_array: Convert to std::array

Example

#include <utxcpp/core.hpp>
#include <array>

int main()
{
	float a1[] = {1.1, 2.2, 3.3};
	std::array<float, sizeof a1/sizeof a1[0]> a11 = std::to_array(a1);
	utx::print_all(a11);
	
	auto a22 = std::to_array({1,2,3,4,5});
	utx::print_all(a22);
}

Top





Coroutines

________________________________________________________________________

co_await - co_yield - co_return

co_await

co_await - The unary operator co_await suspends a coroutine and returns control to the caller.
https://en.cppreference.com/w/cpp/language/coroutines#co_await

co_await expression;

asio::co_spawn - Spawn a new coroutined-based thread of execution.

asio::awaitable - The return type of a coroutine or asynchronous operation.

Example

#include <boost/asio.hpp>
#include <utxcpp/core.hpp>
#include <future>

namespace asio = boost::asio;

// fn must have co_await
asio::awaitable<void> fn()
{
	utx::print(123);
	co_await 0;
	utx::print(321);
}

// func must have co_await
asio::awaitable<void> func(float x)
{
	utx::print(x);
	co_await fn();
	utx::print("Hello, c++!");
}

int main()
{
	asio::io_context io_context;
	asio::co_spawn(io_context, func(3.14f), asio::detached);
	asio::co_spawn(io_context, fn(), asio::detached);
	
	auto f1 = std::async(std::launch::async, [&] {io_context.run();});
	auto f2 = std::async(std::launch::async, [&] {io_context.run();});
}

/*
One Possible Output:
3.140000
123
321
123Hello, c++!

321
*/

co_yield

co_yield - co_yield expression returns a value to the caller and suspends the current coroutine: it is the common building block of resumable generator functions.
https://en.cppreference.com/w/cpp/language/coroutines#co_yield

asio::experimental::coro - The main type of a resumable coroutine.

asio::this_coro::executor - Awaitable object that returns the executor of the current coroutine.

Example

#include <boost/asio.hpp>
#include <boost/asio/experimental/coro.hpp>
#include <utxcpp/core.hpp>

namespace asio = boost::asio;
template <typename T> using asio_coro = asio::experimental::coro<T>;

asio_coro<utx::i32> fn(asio::any_io_executor, utx::i32 & x)
{
	while (true)
	{
		co_yield x++;
	}
}

asio::awaitable<void> func(utx::i32 x)
{
	x = x*x;
	auto object = fn(co_await asio::this_coro::executor, x);
	for (utx::i32 i=0; i<10; i++)
	{
		auto next = co_await object.async_resume(asio::use_awaitable);
		utx::printnl(*next, "");
	}
	utx::print();
}

int main()
{
	asio::io_context io_context;
	asio::co_spawn(io_context, func(3), asio::detached);
	io_context.run();
}

/*
Output:
9 10 11 12 13 14 15 16 17 18
*/

co_return

co_return - return statement in a coroutine (since C++20)

Example

#include <boost/asio.hpp>
#include <boost/asio/experimental/coro.hpp>
#include <utxcpp/core.hpp>

namespace boost::asio
{
	using experimental::coro;
}

namespace asio = boost::asio;

asio::coro<void, int> func(asio::io_context &, int x)
{
	co_return x*x;
}

int main()
{
	asio::io_context io_context;
	auto object = func(io_context, 7);
	object.async_resume(
		[&] (std::exception_ptr, int x)
		{
			utx::print(x); // 49
			object.async_resume(
				[&] (std::exception_ptr, int y)
				{
					utx::print(y); // 0
				}
			);
		}
	);
	io_context.run();
}

Top









Last revised: March 27, 2023 at 03:21:19 GMT


PrevUpHomeNext