Hatena::ブログ(Diary)

bettamodokiのメモ このページをアンテナに追加 RSSフィード

"betta" means Siamese fighting fish (Betta splendens)
and "modoki" means resemblance or imitation in Japanese. So what?

2017-06-05

C++のif条件式の中での変数宣言

12:21 |  C++のif条件式の中での変数宣言を含むブックマーク  C++のif条件式の中での変数宣言のブックマークコメント

dynamic_castを利用する際に以下のように書く場面が出てくる.

Base* base = new DerivA();

if (DerivA* derivA = dynamic_cast<DerivA*>(base))
{
  ; // do something with derivA
}

if (DerivB* derivB = dynamic_cast<DerivB*>(base))
{
  ; // do something with derivB
}

この場合, 二度目のdynamic_castは失敗するため, if文の中身も実行されない.

しかし, 実際のところこれはこの宣言式の何を判断しているのか? この記法について書いている人は多かったのだがそれを説明している人は少なさそうだったので調べてみた. 規格書(?)*1を確認してみると

condition:
      expression
      attribute-specifier-seqopt decl-specifier-seq declarator = initializer-clause
      attribute-specifier-seqopt decl-specifier-seq declarator braced-init-list

となっており, 確かに変数宣言が許可されている(p.125). ちなみにだが, parenthesized initializerは許可されていない. DerivA* derivA(dynamic_cast<DerivA*>(base))とは書けない.

では, 判定はどうなっているかというと, 皆様ご想像の通り,

The value of a condition that is an initialized declaration in a statement other than a switch statement is the value of the declared variable contextually converted to bool (Clause 4). If that conversion is ill-formed, the program is ill-formed.

switchで使う場合以外は, declarator(上で宣言された変数)をboolにキャストしてその値を使う, とある. キャストできない変数の宣言は許されない. というわけで, dynamic_castが成功した時だけ実行されるのは, dynamic_castが失敗したときにnullptrを返し, nullptrはbool値としてはfalseになるためである.

単にスコープの目的だけでここに変数宣言することは意味もないし危険である.

if (int x = 1)
{
  ; // play with 1
}

if (int x = 0)
{
  ; // play with 0
}

二つ目のif文の中身は実行されない. こんなん間違えないよ, という人もいるかもしれないが, この記法について調べたときには以下のような例がわんさと出てきた.

if (double x = some_function(y, z))
{
  ; // this works only if x is not zero.
}

これを意図的に使えないこともない*2が, はっきり言って僕は分かりにくいと思うのでポインタ以外では使わない方が良いのではないか.

*1http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

*2:独自のクラスでboolへのキャストを実装しておくとか

2017-05-31

Cythonで'bool' is not a constant, variable or function identifier

12:31 |  Cythonで'bool' is not a constant, variable or function identifierを含むブックマーク  Cythonで'bool' is not a constant, variable or function identifierのブックマークコメント

Cythonである変数がbool型であるかを調べるのに"isinstance(val, bool)"のようにしたところ, "'bool' is not a constant, variable or function identifier"と怒られた.

どうも, Cythonで扱うbool型には二種類あるらしく*1, libcppのboolを用いているとこのエラーが出る. Pythonにおけるbool型を得たい場合はcpythonのものを利用するようにすれば良い.

from libcpp import bool
from cpython import bool as bool_t
val = True
if isinstance(val, bool_t):
    do_something(<bool> val)

https://github.com/cython/cython/wiki/FAQ#how-do-i-declare-an-object-of-type-bool

*1:bintも加えれば3種類.

2017-02-16

KinesisキーボードでCtrl, Alt/Option, Windows/Commandがおかしなことになったとき

18:22 |  KinesisキーボードでCtrl, Alt/Option, Windows/Commandがおかしなことになったときを含むブックマーク  KinesisキーボードでCtrl, Alt/Option, Windows/Commandがおかしなことになったときのブックマークコメント

どうすれば良いかが下のページに書いてある.

https://www.kinesis-ergo.com/support/technical-support/troubleshoooting-advantage/

"=w", "=m", "=p"のいずれかを同時押しするとキーボードのモードが変わるらしい. 時々気が付かずにやらかすのでメモ.

2016-10-06

CMakeで現状のインクルードパスを文字列として得るには

17:32 |  CMakeで現状のインクルードパスを文字列として得るにはを含むブックマーク  CMakeで現状のインクルードパスを文字列として得るにはのブックマークコメント

インクルードパスをCMakeで指定するにはinclude_directoriesを利用するが, 逆に今その関数によってどこが追加されているかを文字列として取得したい.

まず, その情報自体はINCLUDE_DIRECTORIESにあるが, これはプロパティであって(環境)変数ではない. そこで一旦これをとってくる.

https://cmake.org/cmake/help/v3.0/command/include_directories.html#command:include_directories

get_directory_property(INCLUDE_PATH INCLUDE_DIRECTORIES)

http://stackoverflow.com/questions/6902149/listing-include-directories-in-cmake

ただし, ここで得たINCLUDE_PATHはリストなので, "/path/to/A:/path/to/B"のような形にするにはリストを連結しなければならない.

set(_TMP_RESULT "")
set(_GLUE "") # effective glue is empty at the beginning
foreach(SINGLE_PATH ${INCLUDE_PATH})
  set(_TMP_RESULT "${_TMP_RESULT}${_GLUE}${SINGLE_PATH}")
  set(_GLUE ":") # delimiter
endforeach()
set(INCLUDE_PATH ${_TMP_RESULT})
# message("${INCLUDE_PATH}")

http://stackoverflow.com/questions/7172670/best-shortest-way-to-join-a-list-in-cmake

これでインクルードパスを":"で連結した文字列として得られた.

2016-10-04

Pythonのクラスメンバ変数とメタクラス

18:02 | Pythonのクラスメンバ変数とメタクラスを含むブックマーク Pythonのクラスメンバ変数とメタクラスのブックマークコメント

とりあえず以下のようなプログラムについて考える.

class Reservoir(object):

    def get(self, key):
        value = key * 3  # Do something hard
        return value

if __name__ == "__main__":
    obj = Reservoir()
    print(obj.get('KEY1'))  # => KEY1KEY1KEY1
    print(obj.get('KEY2'))  # => KEY2KEY2KEY2
    print(obj.get('KEY1'))  # => KEY1KEY1KEY1

このget操作がコストを必要とする場合, キャッシュすることを考える. この場合, メンバ変数を使って以下のようにする.

import logging

class Reservoir(object):

    def __init__(self):
        self.DATA = {}

    def get(self, key):
        if key in self.DATA.keys():
            logging.info('The key [{}] is already processed.'.format(key))
            return self.DATA[key]
        logging.info('Processing the key [{}].'.format(key))
        value = key * 3  # Do something hard
        self.DATA[key] = value
        return value

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)

    obj = Reservoir()
    print(obj.get('KEY1'))  # => INFO:root:Processing the key [KEY1].
                            # => KEY1KEY1KEY1
    print(obj.get('KEY2'))  # => INFO:root:Processing the key [KEY2].
                            # => KEY2KEY2KEY2
    print(obj.get('KEY1'))  # => INFO:root:The key [KEY1] is already processed.
                            # => KEY1KEY1KEY1

ただし, これだとあくまでもインスタンス化されたオブジェクトに記憶されるだけなので他の場所で呼びだしても参照されない.

obj1 = Reservoir()
print(obj1.get('KEY1'))  # => INFO:root:Processing the key [KEY1].
                         # => KEY1KEY1KEY1
obj2 = Reservoir()
print(obj2.get('KEY1'))  # => INFO:root:Processing the key [KEY1].
                         # => KEY1KEY1KEY1

オブジェクトのメンバ変数ではなく、クラスのメンバ変数にしてやればインスタンス間で共有できる.

class Reservoir(object):

    DATA = {}

    def get(self, key):
        # same as above ...

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)

    print(Reservoir().get('KEY1'))  # => INFO:root:Processing the key [KEY1].
                                    # => KEY1KEY1KEY1
    print(Reservoir().get('KEY1'))  # => INFO:root:The key [KEY1] is already processed.
                                    # => KEY1KEY1KEY1

しかし次にこの手のクラスを継承して別のクラスを作ることを考える. 例えば以下のようにA, Bふたつの異なる処理をするクラスを考えると,

class Reservoir(object):

    DATA = {}

    def get(self, key):
        if key in self.DATA.keys():
            return self.DATA[key]
        value = self.process(key)
        self.DATA[key] = value
        return value

    def process(self, key):
        raise NotImplementedError()

class A(Reservoir):

    def process(self, key):
        return 'A processed the key, {}.'.format(key)

class B(Reservoir):

    def process(self, key):
        return 'B processed the key, {}.'.format(key)

if __name__ == "__main__":
    print(A().get('KEY1'))  # => A processed the key [KEY1].
    print(B().get('KEY1'))  # => A processed the key [KEY1].

同じクラスを継承しているため, 異なるクラス間で結果を共有してしまう. これはこれで正しい挙動だが, 今回の場合, AとBの結果は別々に記憶しておきたい.

そこでメタクラスを使う.

import logging

class ReservoirMeta(type):

    def __new__(cls, name, bases, attrs):
        attrs['DATA'] = {}

        def _get(self, key):
            if key in self.DATA.keys():
                logging.info("A cache for the key [{}] works.".format(key))
                return self.DATA[key]
            value = self.process(key)
            self.DATA[key] = value
            return value
        attrs['get'] = _get

        if 'process' not in attrs.keys():
            def _process(self, key):
                raise NotImplementedError()
            attrs['process'] = _process

        return super().__new__(cls, name, bases, attrs)

class A(metaclass=ReservoirMeta):

    def process(self, key):
        return 'A processed the key [{}].'.format(key)

class B(metaclass=ReservoirMeta):

    def process(self, key):
        return 'B processed the key [{}].'.format(key)

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)

    print(A().get('KEY1'))  # => A processed the key [KEY1].
    print(B().get('KEY1'))  # => B processed the key [KEY1].
    print(A().get('KEY1'))  # => INFO:root:A cache for the key [KEY1] works.
                            # => A processed the key [KEY1].
    print(B().get('KEY1'))  # => INFO:root:A cache for the key [KEY1] works.
                            # => B processed the key [KEY1].

ReservoirMetaはReservoir相当の機能を備えたテンプレートとなってAやBを生成する. 言わば後付けでReservoirMetaの機能がクラスに付与される. というわけで継承とは異なる. 実際,

print(A.__bases__)  # => (<class 'object'>,)

ただ後付けしているだけなのでデコレータでもいける. というか, 意味合い的に同じものだと考えて良いのかもしれない.

def reservoir(cls):
    setattr(cls, 'DATA', {})

    def _get(self, key):
        if key in self.DATA.keys():
            logging.info("A cache for the key [{}] works.".format(key))
            return self.DATA[key]
        value = self.process(key)
        self.DATA[key] = value
        return value
    setattr(cls, 'get', _get)

    if not hasattr(cls, 'process'):
        def _process(self, key):
            raise NotImplementedError()
        setattr(cls, 'process', _process)

    return cls

@reservoir
class A:
    # same as above ...

というわけでメタクラスの感覚をつかむために, ここまでさんざんやってきたがデコレータに戻ってきてしまった. というか, この事例ではメンバ関数にデコレータを直接かませば十分である.

import logging

def reservoir_deco(func):
    DATA = {}
    def wrapped(self, key):
        if key in DATA.keys():
            logging.info("A cache for the key [{}] works.".format(key))
            return DATA[key]
        value = func(self, key)
        DATA[key] = value
        return value
    return wrapped

class A(object):

    @reservoir_deco
    def get(self, key):
        return 'A processed the key [{}].'.format(key)

class B(object):

    @reservoir_deco
    def get(self, key):
        return 'B processed the key [{}].'.format(key)

if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)

    print(A().get('KEY1'))  # => A processed the key [KEY1].
    print(B().get('KEY1'))  # => B processed the key [KEY1].
    print(A().get('KEY1'))  # => INFO:root:A cache for the key [KEY1] works.
                            # => A processed the key [KEY1].
    print(B().get('KEY1'))  # => INFO:root:A cache for the key [KEY1] works.
                            # => B processed the key [KEY1].