プログラミング言語C++標準規格の索引(Index)に紛れ込むリビングデッド。🧠👀🧟*1
brains
names that want to eat your, [zombie.names]living dead
https://github.com/cplusplus/draft/commit/e844e0f45550eb0bf11ea262e4abd8a5403f47d4
name of, [zombie.names]
関連URL
プログラミング言語C++標準規格の索引(Index)に紛れ込むリビングデッド。🧠👀🧟*1
brains
names that want to eat your, [zombie.names]living dead
https://github.com/cplusplus/draft/commit/e844e0f45550eb0bf11ea262e4abd8a5403f47d4
name of, [zombie.names]
関連URL
プログラミング言語Cの次期仕様C2yに向けて、式の評価順序を厳格に規定する提案 N3203 Strict order of expression evaluation が提出されている。チャレンジングなお話。
C言語(C++も同様)では歴史的経緯から、演算子オペランドの評価順*1や関数実引数リストの評価順(→id:yohhoy:20120304)は規定されておらず、任意の順序で実行される可能性がある。*2
C/C++以降のプログラミング言語では演算子オペランドや関数実引数リストの評価順「左→右」と保証されており、本提案がC言語に採用されれば未規定(unspecified)動作や未定義動作(undefined behavior)の回避に大きく貢献するはず。いや、でも厳しそうだなぁ...
int f() { puts("f"); return 1; } int g() { puts("g"); return 2; } void h(int, int) {} int a = f() + g(); // a==3 // C23: 標準出力は f→g / g→f 順のいずれか // N3203: 必ず f→g の順を保証する h( f(), g() ); // C23: 標準出力は f→g / g→f 順のいずれか // N3203: 必ず f→g の順を保証する
関連URL
C++2c(C++26)標準ライブラリに追加される飽和演算(saturation arithmetic)サポートについてメモ。
// C++2c <numeric>ヘッダ namespace std { // T,U = 符号付き整数型 or 符号無し整数型 template<class T> constexpr T add_sat(T x, T y) noexcept; template<class T> constexpr T sub_sat(T x, T y) noexcept; template<class T> constexpr T mul_sat(T x, T y) noexcept; template<class T> constexpr T div_sat(T x, T y) noexcept; template<class T, class U> constexpr T saturate_cast(U x) noexcept; }
関数 | 効果 |
---|---|
add_sat(x,y) |
飽和加算(x + y) |
sub_sat(x,y) |
飽和減算(x - y) |
mul_sat(x,y) |
飽和乗算(x * y) |
div_sat(x,y) |
飽和除算(x / y) |
saturate_cast<T>(x) |
型キャスト飽和演算 |
メモ:信号処理分野では飽和演算が多用されるため、例えばOpenCVライブラリでは型キャスト飽和演算cv::saturate_cast<T>(x)
が提供される。C++2c標準ライブラリ版はOpenCV版とは異なり、浮動小数点数からの型変換をサポートしない。*1
関連URL
プログラミング言語C++において、異なる基底クラスに属する同名メンバ関数*1を個別にオーバーライドする方法。
// 同名メンバ関数をカスタマイズポイントとして提供する // 互いに無関係なインタフェースクラス struct Interface1 { virtual void process() = 0; }; struct Interface2 { virtual void process() = 0; }; // インタフェース実装クラス struct Derived: Interface1, Interface2 { void process() override { /*...*/ } // Interface1::process と Interface2::process を // 同時にオーバーライドするため実装分離は不可能 };
インタフェース別にprocess
メンバ関数オーバーライドする中間クラスを定義し、別名メンバ関数(process1
, process2
)への処理委譲によって個別オーバーライドを可能とする。
// メンバ関数名を変換するプロキシインタフェース struct ProxyInterface1 : Interface1 { void process() override final { return process1(); } virtual void process1() = 0; }; struct ProxyInterface2 : Interface2 { void process() override final { return process2(); } virtual void process2() = 0; }; // インタフェース実装クラス struct Derived: ProxyInterface1, ProxyInterface2 { void process1() override { /*A*/ } void process2() override { /*B*/ } // 下記はコンパイルエラーとして検出 // void process() override {} }; Derived obj; Interface1& if1 = obj; if1.process(); // A Interface2& if2 = obj; if2.process(); // B
関連URL
*1:厳密には同一シグネチャ(→id:yohhoy:20210927)をもつ仮想関数。
プログラミング言語C/C++の次期標準規格C2x(C23)およびC++2c(C++26)では、アサーションマクロassert
の改善が行われる。
#include <assert.h> // C/C++ #include <cassert> // C++のみ int is_valid(int); assert( "42 shall be vaild", is_valid(42) ); // NG: C17/C++20現在 // OK: C2x/C++2c以降 assert(("42 shall be vaild", is_valid(42))); // OK: 式全体を括弧で囲う
C言語ではトラブルを引き起こすケースは少ないが*1、C++言語では初期化子リストやテンプレートパラメータなどでトラブル発生頻度が高い。はず。
// C++事例 assert( get_vec() == std::vector{1, 2, 3} ); assert( calc_mul<int,2>(3) == 6 );
本件は遡及適用されるバグ修正ではなく、新機能として取り扱われる模様。JTC1/SC22/WG14 2022年1-2月会議録*2より引用:
5.29 Sommerlad, Make assert() macro user friendly for C and C++ v2 [N 2829]
Discussion of how the current wording says "scalar expression" so this could be seen as a bug fix.Concerns about making this a special macro definition vs all the other macros in the standard. Makes it inconsistent. Counters included incremental improvement or that this was the only macro that is a problem (author mentioned specifically for C++).
関連URL
C++17以降のC++標準ライブラリ仕様では、signal
関数のプロトタイプ宣言が読みやすく書き直されている。
C++標準ライブラリ仕様としてプロトタイプ宣言が行われている。C++17 21.10.3より宣言を引用:
Header <csignal> synopsis
namespace std { // 21.10.4, signal handlers extern "C" using signal-handler = void(int); // exposition only signal-handler* signal(int sig, signal-handler* func); }The contents of the header <csignal> are the same as the C standard library header <signal.h>.
またC++標準において、シグナルハンドラ内で行っても良い Signal-safe 操作の定義がなされている。C++17 21.10.4/p3より引用:
An evaluation is signal-safe unless it includes one of the following:
- a call to any standard library function, except for plain lock-free atomic operations and functions explicitly identified as signal-safe. [Note: This implicitly excludes the use of
new
anddelete
expressions that rely on a library-provided memory allocator. -- end note]- an access to an object with thread storage duration;
- a
dynamic_cast
expression;- throwing of an exception;
- control entering a try-block or function-try-block;
- initialization of a variable with static storage duration requiring dynamic initialization (6.6.3, 9.7); or
- waiting for the completion of the initialization of a variable with static storage duration (9.7).
A signal handler invocation has undefined behavior if it includes an evaluation that is not signal-safe.
C++14以前の仕様では独自のプロトタイプ宣言を行わず、標準Cヘッダ<signal.h>を参照している。
シグナルハンドラ内からの(一部例外を除く)C++関数呼び出しは処理系定義(implementation-defined)とされ、Signal-safeに関する言及は存在しない。C++14 18.10/p10より一部引用:
The common subset of the C and C++ languages consists of all declarations, definitions, and expressions that may appear in a well formed C++ program and also in a conforming C program. A POF ("plain old function") is a function that uses only features from this common subset, and that does not directly or indirectly use any function that is not a POF, except that it may use plain lock-free atomic operations. (snip) All signal handlers shall have C linkage. The behavior of any function other than a POF used as a signal handler in a C++ program is implementation-defined.228
脚注228) In particular, a signal handler using exception handling is very likely to have problems. Also, invokingstd::exit
may cause destruction of objects, including those of the standard library implementation, which, in general, yields undefined behavior in a signal handler (see 1.9).
C17 7.14.1.1/p1より宣言を引用:
Synopsis
#include <signal.h> void (*signal(int sig, void (*func)(int)))(int);
シグナルハンドラ内で許可される操作は、条件付きで明示的に列挙されている。C17 7.14.1.1/p4-5より一部引用:
4 If the signal occurs as the result of calling the
abort
orraise
function, the signal handler shall not call theraise
function.
5 If the signal occurs other than as the result of calling theabort
orraise
function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared asvolatile sig_atomic_t
, or the signal handler calls any function in the standard library other than (snip)
関連URL