yohhoyの日記

技術的メモをしていきたい日記

C++標準ライブラリのビット処理関数群

C++2a(C++20)標準ライブラリに追加される <bit> ヘッダについてメモ。

2020-05-11追記:(PDF)P1956R1が採択され*1、関数名が大幅に変更(ispow2has_single_bitceil2bit_ceilfloor2bit_floorlog2p1bit_width)された。"2の冪乗(powers of two)" への言及回避が目的とのこと...*2

// C++2a <bit>ヘッダ
namespace std {
  // sizeof(To) == sizeof(From)
  template<typename To, typename From>
    constexpr To bit_cast(const From& from) noexcept;

  // T = 符号なし整数型
  template <class T>
    constexpr bool has_single_bit(T x) noexcept;
  template <class T>
    constexpr T bit_ceil(T x);  // P1355R2変更
  template <class T>
    constexpr T bit_floor(T x) noexcept;
  template <class T>
    constexpr T bit_width(T x) noexcept;
}

2019-08-30追記:2019年7月会合で採択されたP1355R2によりceil2関数テンプレートからnoexcept指定が削除された。

関数 効果
bit_cast<To>(n) ビット表現を維持した型キャスト
(安全なreinterpret_cast)
has_single_bit(n) nが "2の冪乗" か否かを判定
bit_ceil(n) n以上で最小の "2の冪乗"
bit_floor(n) n以下で最大の "2の冪乗"
bit_width(n) 1 + floor(log2(n)) の整数演算*3
(ビット幅長)

入力値nと関数戻り値(n=0..16):

n has_single_bit bit_ceil bit_floor bit_width
0 false 1 0 0
1 true 1 1 1
2 true 2 2 2
3 false 4 2 2
4 true 4 4 3
5..7 false 8 4 3
8 true 8 8 4
9..15 false 16 8 4
16 true 16 16 5

関連URL

*1:https://github.com/cplusplus/draft/pull/3780

*2:P1956R1引用:"LEWG voted to rename some of the bit manipulation functions in order to avoid references to powers of two since manipulating bits should work in the future on bytes and machine words that are not numbers and for which numeric operations are not defined"

*3:初期提案P0556R0では “前提条件(Requires) n>0 あり log2(n) 関数” と定義されていたが、log2p1 に改名されC++標準ライブラリの広い契約(Wide Contracts)を満たす(→id:yohhoy:20180127)よう仕様変更された。

名無しの変数

プログラミング言語C++には、言語仕様の定義上 “名前のない変数(variable)” が存在する。誰得情報。

try {
} catch (std::exception&) {  // ★
}

C++14 3/p6より引用(下線部は強調)。C++17では6/p6。

A variable is introduced by the declaration of a reference other than a non-static data member or of an object. The variable's name, if any, denotes the reference or object.

同Wordingを導入したCWG DR 1769より一部引用。

Additional discussion has pointed out that, although the unnamed handler parameter is no longer called a "temporary" in the proposed resolution, 15.2 [class.temporary] paragraph 1 still lists "entering a handler (18.3 [except.handle])" as one of the contexts in which a temporary is created. A question was also raised as to whether it is necessary to deal with named and unnamed handler parameters separately, since both are now referred to as "variables" in the revised wording. This issue has therefore been returned to "review" status to allow consideration of these points.

関連URL

Is volatile useful with threads?

volatileキーワードはマルチスレッド処理に役立つ?.com(http://isvolatileusefulwiththreads.com/

C
http://c.isvolatileusefulwiththreads.com/
C++
http://cxx.isvolatileusefulwiththreads.com/
Java
http://java.isvolatileusefulwiththreads.com/
C#
http://csharp.isvolatileusefulwiththreads.com/

関連URL

条件付きexplicit指定子

C++2a(C++20)言語仕様では 条件付きexplicit指定子 が追加され、pairtupleなどの型Tを値保持するクラステンプレートにおいて、型Tコンストラクタ実装時のexplicit性継承(→id:yohhoy:20150416)が容易になる。

// C++17仕様
template <typename T1, typename T2>
struct pair {
  // non-explicitコンストラクタ
  template <typename U1=T1, typename U2=T2,
    std::enable_if_t<
      std::is_constructible_v<T1, U1> &&
      std::is_constructible_v<T2, U2> &&
      std::is_convertible_v<U1, T1> &&
      std::is_convertible_v<U2, T2>
    , int> = 0>
  constexpr pair(U1&&, U2&&);
  
  // explicitコンストラクタ
  template <typename U1=T1, typename U2=T2,
    std::enable_if_t<
      std::is_constructible_v<T1, U1> &&
      std::is_constructible_v<T2, U2> &&
      !(std::is_convertible_v<U1, T1> &&
        std::is_convertible_v<U2, T2>)
     , int> = 0>
  explicit constexpr pair(U1&&, U2&&);
};
// C++2a仕様: Concept+explicit(bool)
template <typename T1, typename T2>
struct pair {
  // explicit指定有無はT1, T2, U1, U2型に依存
  template <typename U1=T1, typename U2=T2>
    requires std::is_constructible_v<T1, U1> &&
             std::is_constructible_v<T2, U2>
  explicit(!std::is_convertible_v<U1, T1> ||
           !std::is_convertible_v<U2, T2>)
  constexpr pair(U1&&, U2&&);
};

関連URL

配列有効範囲外を指すポインタ値は存在が許されない

プログラミング言語C/C++における、配列型とポインタ演算の知られざる*1落とし穴。

問題:下記sum_odd関数の実行結果は?

int data[5] = { 1, 2, 3, 4, 5 };

// 奇数番目の要素値のみを合計する
int sum_odd()
{
  int s = 0;
  int *endp = data + 5;  // 配列末尾要素の次位置

  // ポインタ値p が endp を越えるまでループ処理
  for (int *p = data; p < endp; p += 2) {
    s += *p;
  }
  return s;
}

答え:未定義動作(undefined behavior)。C/C++こわい(((;´・ω・`))) これがC/C++だ( ・`ω・´)キリッ。

C/C++では、ポインタ値は “有効な配列要素”*2 もしくは “配列末尾要素の次位置(one past the last element)” しか指してはならない。それ以外の値、例えば前掲コードのように配列末尾要素の2つ後ろ(のつもり)などは、加減算によりポインタ値が算出された時点で未定義動作と規定される。

C

C11 6.5.6/p8より引用(下線部は強調)。C99でも6.5.6/p8。

8 When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the pointer operand points to an element of an array object, and the array is large enough, the result points to an element offset from the original element such that the difference of the subscripts of the resulting and original array elements equals the integer expression. In other words, if the expression P points to the i-th element of an array object, the expressions (P)+N (equivalently, N+(P)) and (P)-N (where N has the value n) point to, respectively, the i+n-th and i-n-th elements of the array object, provided they exist. Moreover, if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object, and if the expression Q points one past the last element of an array object, the expression (Q)-1 points to the last element of the array object. If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated.

C++

C++17 8.7/p4より引用(下線部は強調)。C++14/11/03では5.7/p5。

4 When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand. If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j) point to the (possibly-hypothetical) element x[i + j] if 0 ≤ i + jn; otherwise, the behavior is undefined. Likewise, the expression P - J points to the (possibly-hypothetical) element x[i - j] if 0 ≤ i - jn; otherwise, the behavior is undefined.

関連URL

*1:要出典。C言語 == 高級アセンブラ という認識では危ないというお話。

*2:ポインタ値が指す先は、当然 int など非配列のデータ型オブジェクトでも良い。

Type Juggling

プログラミング言語PHPにおける奇妙な型変換 "Type Juggling"。文字列と数値の比較(==)には要注意。
2022-02-04追記:本記事で取り上げた型変換はPHP 7.4系までの言語仕様。PHP 8.0.0以降ではいずれもfalse値となる。PHP公式の下方互換性に関するドキュメント解説記事を参照のこと。

<?php
var_dump(0 == "php");   // bool(true) !?
var_dump(0 === "php");  // bool(false)
?>

PHP公式マニュアルより言語リファレンス該当箇所を引用。

整数値を文字列と比較したり、比較に数値形式の文字が含まれる場合は、文字列が数値に変換され、数値としての比較を行います。

http://php.net/manual/ja/language.operators.comparison.php

文字列の最初の部分により値が決まります。文字列が、有効な数値データから始まる場合、この値が使用されます。その他の場合、値は 0 (ゼロ) となります。有効な数値データは符号(オプション)の後に、1つ以上の数字 (オプションとして小数点を1つ含む)、オプションとして指数部が続きます。指数部は 'e' または 'E' の後に1つ以上の数字が続く形式です。

http://php.net/manual/ja/language.types.string.php#language.types.string.conversion

関連URL

関数呼び出しのトレース

Pythonスクリプトにおいて、非侵襲的に関数呼び出しと戻り値をトレースする方法。

#!/bin/usr/env python3
import inspect
import sys

def tracer(frame, event, arg, depth=[0]):
  f_name = frame.f_code.co_name
  if f_name == '_ag':  # dirty hack for CPython 3.6+
    return

  if event == 'call':
    depth[0] += 1
    print('>' * depth[0], f_name, inspect.formatargvalues(*inspect.getargvalues(frame)))
    return tracer
  elif event == 'return':
    print('<' * depth[0], f_name, arg)
    depth[0] -= 1

sys.settrace(tracer)
# 以降の関数呼び出しをトレース対象とする

def fib(n):
  if n <= 2:
    return 1
  return fib(n - 1) + fib(n - 2)

result = fib(5)

出力結果:

> fib (n=5)
>> fib (n=4)
>>> fib (n=3)
>>>> fib (n=2)
<<<< fib 1
>>>> fib (n=1)
<<<< fib 1
<<< fib 2
>>> fib (n=2)
<<< fib 1
<< fib 3
>> fib (n=3)
>>> fib (n=2)
<<< fib 1
>>> fib (n=1)
<<< fib 1
<< fib 2
< fib 5

関連URL