メモの日々


2023年08月03日(木) [長年日記]

[c++][windows] VC++で __FILE__ マクロの値を相対パスにする

Visual Studio 2019で次のコードをビルドすると __FILE__ マクロの値が絶対パスで出力される。

#include <iostream>

int main()
{
    std::cout << __FILE__ << "\n";
}
C:\Users\kenichi\source\repos\ConsoleApplication1\d1trimfile\main.cpp

コンパイルオプションに

/d1trimfile:"$(SolutionDir)\"

を追加すると、上記の結果が相対パスに変わることを知った。

d1trimfile\main.cpp

このコンパイルオプションについてのリファレンスは見つけることができなかった。言及しているページをメモ。


2023年08月15日(火) [長年日記]

[python] pytestのパラメータ化テストでパラメータ名に日本語を使用する設定

pytestでParametrizing fixturesを使った次のテストを実行すると、パラメータの識別子として表示させたい日本語部分がエスケープされてしまった。

import pytest

data = [
    ("テスト", "データ1"),
    ("テスト", "データ2"),
    ("テスト", "データ3"),
]

def data_name(d):
    return f"{d[0]}-{d[1]}"

@pytest.fixture(params=data, ids=data_name)
def parameter(request):
    return request.param

def test_sample(parameter):
    assert True
$ pytest -v test_1.py
=============================== test session starts ===============================
platform linux -- Python 3.11.1, pytest-7.3.1, pluggy-1.0.0 -- /home/kenichi/.anyenv/envs/pyenv/versions/3.11.1/bin/python3.11
cachedir: .pytest_cache
rootdir: /home/kenichi/work/python/pytest
collected 3 items

test_1.py::test_sample[\u30c6\u30b9\u30c8-\u30c7\u30fc\u30bf1] PASSED       [ 33%]
test_1.py::test_sample[\u30c6\u30b9\u30c8-\u30c7\u30fc\u30bf2] PASSED       [ 66%]
test_1.py::test_sample[\u30c6\u30b9\u30c8-\u30c7\u30fc\u30bf3] PASSED       [100%]

================================ 3 passed in 0.01s ================================

調べると、

に次の説明があった。

pytest by default escapes any non-ascii characters used in unicode strings for the parametrization because it has several downsides. If however you would like to use unicode strings in parametrization and see them in the terminal as is (non-escaped), use this option in your pytest.ini:

[pytest]
disable_test_id_escaping_and_forfeit_all_rights_to_community_support = True

Keep in mind however that this might cause unwanted side effects and even bugs depending on the OS used and plugins currently installed, so use it at your own risk.

リスクはあるようだが、pytest.iniを用意してそこに上記の設定を書くことで、次のように日本語が表示されるようになった。

$ pytest -v test_1.py
=============================== test session starts ===============================
platform linux -- Python 3.11.1, pytest-7.3.1, pluggy-1.0.0 -- /home/kenichi/.anyenv/envs/pyenv/versions/3.11.1/bin/python3.11
cachedir: .pytest_cache
rootdir: /home/kenichi/work/python/pytest
configfile: pytest.ini
collected 3 items

test_1.py::test_sample[テスト-データ1] PASSED                               [ 33%]
test_1.py::test_sample[テスト-データ2] PASSED                               [ 66%]
test_1.py::test_sample[テスト-データ3] PASSED                               [100%]

================================ 3 passed in 0.01s ================================

2023年08月23日(水) [長年日記]

[c++] std::chronoメモ

std::chronoについてメモ。C++20で色々機能が追加されており、それを前提とする。

durationは時間

durationは時間を表すクラステンプレート。テンプレートパラメータとして値の型と、値の1が何秒を表すかの比率を持つ。

テンプレートをインスタンス化したクラスとして seconds、milliseconds、years などの色々な型が用意されている。なお、yearsは31556952秒、monthsは2629746秒という「平均的な長さ」が単位になっていることに注意。

sなどのリテラル演算子も用意されており、これらを使ってオブジェクトを作ることもできる。

durationクラス間の変換は、精度が落ちなければコンストラクタや代入で行え、精度が落ちる場合はduration_cast()floor()などを使い分けて行う。

#include <chrono>
#include <iostream>

int main()
{
    using namespace std::literals::chrono_literals;
    namespace ch = std::chrono;

    const ch::duration<int> d1{ -100 };
    const ch::milliseconds d2 = d1;
    const auto d3 = ch::duration_cast<ch::minutes>(d1);
    const auto d4 = ch::floor<ch::minutes>(d1);
    const auto d5 = d4 + 1s;
    const ch::months d6{ 1 };
    const ch::years d7{ 1 };

    std::cout << "d1 = " << d1 << std::endl;
    std::cout << "d2 = " << d2 << std::endl;
    std::cout << "d3 = " << d3 << std::endl;
    std::cout << "d4 = " << d4 << std::endl;
    std::cout << "d5 = " << d5 << std::endl;
    std::cout << "d6 = " << d6 << std::endl;
    std::cout << "d7 = " << d7 << std::endl;
}
d1 = -100s
d2 = -100000ms
d3 = -1min
d4 = -2min
d5 = -119s
d6 = 1[2629746]s
d7 = 1[31556952]s

time_pointは時刻

time_pointは時刻を表すクラステンプレート。テンプレートパラメータとして「時計」型と、時刻の刻み幅とするdurationを持つ。

時計型としてsystem_clocklocal_tなどが用意されており、それらを使ってtime_pointテンプレートをインスタンス化したクラスとしてsys_secondslocal_daysなどが用意されている。

時刻は起点となる時刻(エポック)からの経過時間として表現されている。エポックは時計型から決まる。

特定の時刻を生成するには、/ 演算子を使ってyear_month_dayオブジェクトを作り、それをsys_daysに変換するという方法が使える。

time_pointクラス間の変換もdurationと同様で、精度が落ちなければコンストラクタや代入で行え、精度が落ちる場合はtime_point_cast()floor()などを使い分けて行う。

#include <iostream>

int main()
{
    using namespace std::literals::chrono_literals;
    namespace ch = std::chrono;

    const ch::time_point<ch::system_clock> t1;   // system_clockのエポック
    const ch::sys_seconds t2{ ch::months{ 1 } }; // エポックから約一か月後
    const auto t3 = ch::sys_days{ 2023y/8/15 } + 12h + 34min + 56s;
    const ch::time_point<ch::system_clock, ch::milliseconds> t4 = t3;
    const auto t5 = ch::floor<ch::days>(t3);

    std::cout << "t1 = " << t1 << std::endl;
    std::cout << "t2 = " << t2 << std::endl;
    std::cout << "t3 = " << t3 << std::endl;
    std::cout << "t4 = " << t4 << std::endl;
    std::cout << "t5 = " << t5 << std::endl;
}
t1 = 1970-01-01 00:00:00.0000000
t2 = 1970-01-31 10:29:06
t3 = 2023-08-15 12:34:56
t4 = 2023-08-15 12:34:56.000
t5 = 2023-08-15

時計とタイムゾーン

時計は現在時刻を取得するときに使うクラス。いくつか用意されているが、通常はsystem_clockを使い、経過時間を計る際にsteady_clockを使うくらいか。

system_clock::now()が返す時刻はUTC相当のものであることに注意。システムのタイムゾーンでの時刻を取得するには、current_zone()time_zoneオブジェクトを取得し、そのメンバ関数to_local()を呼び出す方法が使える。

また、時刻をタイムゾーン付きで管理したい場合はzoned_timeクラステンプレートを使う。zoned_timeは時刻を表すがtime_point型ではなく、time_pointとtime_zoneのペアである。

#include <chrono>
#include <iostream>

int main()
{
	namespace ch = std::chrono;

	const auto now1 = ch::system_clock::now();
	const auto now2 = ch::current_zone()->to_local(now1);
	const ch::zoned_time now3{ ch::current_zone(), now1 };

	std::cout << "now1 = " << now1 << std::endl;
	std::cout << "now2 = " << now2 << std::endl;
	std::cout << "now3 = " << now3 << std::endl;
}
now1 = 2023-08-23 10:16:29.2074391
now2 = 2023-08-23 19:16:29.2074391
now3 = 2023-08-23 19:16:29.2074391 JST

2023年08月24日(木) [長年日記]

[c++] std::chrono::time_pointの文字列表現から秒未満を除去する

system_clockから取得する現在時刻を文字列として出力すると秒未満の値も出力されるが、秒未満の値を出力しないようにするにはどうするのか。

std::cout << std::chrono::system_clock::now() << std::endl;
2023-08-23 15:41:17.7805038

std::put_time() でできる

<iomanip>にあるput_time()を使うと一応できる。でも gmtime とか呼びたくない。

#include <chrono>
#include <iomanip>
#include <iostream>
#include <time.h>

int main()
{
    namespace ch = std::chrono;

    const auto now = ch::system_clock::now();
    const auto now_t = ch::system_clock::to_time_t(now);
    tm tm;
    gmtime_s(&tm, &now_t);
    std::cout << std::put_time(&tm, "%F %T") << std::endl;
}
2023-08-23 15:43:08

std::format() でできない

format()でフォーマット指定することで制御できそうなものだが、秒だけを出力する指定子は無いみたい。put_time()とは「%T」(というか「%S」)の挙動が違う。

time_pointのdurationを変更するとできる

time_pointの刻みを粗くして、秒単位の時刻に変換すれば秒未満の値は出力されなくなる。釈然としないが、これが想定されている制御方法のようだ。

#include <chrono>
#include <iostream>

int main()
{
    namespace ch = std::chrono;

    const auto now = ch::system_clock::now();
    const auto now2 = ch::floor<ch::seconds>(now);

    std::cout << now << std::endl;
    std::cout << std::format("{:%F %T}", now) << std::endl;
    std::cout << now2 << std::endl;
    std::cout << std::format("{:%F %T}", now2) << std::endl;
}
2023-08-23 15:43:36.5353184
2023-08-23 15:43:36.5353184
2023-08-23 15:43:36
2023-08-23 15:43:36