kkAyatakaのメモ帳。

誰かの役に立つかもしれない備忘録。

plusctx (処理履歴の保存と参照)

ライブラリというよりは実装アイデアという感じ。ロギングの課題を解決するために考えていところ、構造的に割と面白かったので組んでみた。

組んだ後でスタックトレースで良いのでは?とも思ったけど、まあ、マルチプラットフォームで取ったり、整形したり、任意の情報乗っけたりとか考えたら、一応用途はありそう。

サンプル

  • 関数の冒頭でPLUSCTX_CTX("name");としてコンテキストを定義する
    • コンテキストはスレッド毎 (thread_local) にスタックとして保存される
    • ブロックコープを抜けると (関数を抜けると) スタックから破棄される
  • サブ関数内でコンテキストスタックを確認すると、上位の (呼び出し元) の情報を得ることができる
#include "plusctx/plusctx.hpp"

#include <iostream>
#include <sstream>

// コンテキストスタックから文字列を生成
// スタックはスレッド毎に管理
std::string get_ctx_string() {
    const auto stack = plusctx::get_context_stack();

    std::ostringstream ss;
    for (const auto c : stack) {
        ss << ">" << c->name;
    }

    return ss.str();
}

void sub1();

// main
int main() {
    PLUSCTX_CTX("main");

    // mainを詰んだのでmainだけ
    std::cout << get_ctx_string() << std::endl; // >main

    sub1();

    // コンテキストの寿命はブロック単位なので、
    // sub1コンテキストは削除されてる
    std::cout << get_ctx_string() << std::endl; // >main
}

void sub1() {
    PLUSCTX_CTX("sub1");

    // mainから呼び出されたことがわかる
    std::cout << get_ctx_string() << std::endl; // >main>sub1
}

plusctx::Contextクラス

ロギング用途を想定しているので、コンテキストには関数名やファイル名などを保存している。コンテキスト情報の取り扱いについては既定していないので、どう使うかは別の問題。

// 任意の名前だけでなく、関数名やファイル名も保存する
class Context {
public:
    Context(
        const std::string name,
        const std::string func_name,
        const std::string rich_func_name,
        const std::string file_name,
        const std::size_t line_no
    );

// 簡易にインスタンス化できるようにマクロを準備
#define PLUSCTX_CTX(name) plusctx::Context plusctx_ctx__( \
    name, \
    __func__, \
    __FUNCSIG__, \
    plusctx::detail::shorten_file_name(__FILE__), \
    __LINE__)

その他

  • 個人的に使うかは微妙なラインなのだけど、アイデアは残しておきたかったので組んでみた
  • 実際の運用場面ではアプリケーション段階で決めることが多い。継承して拡張しても良いし、同コンセプトで定義しなおすこともありそう
  • コンテキストの生成時 (コンストラクタ) トレースログ出すとかもあり
  • アプリケーションデータの伝搬のほうも考えたけど、こっちは全体デザインに影響するので適用範囲が限られるのと、簡単にグローバル変数になるので運用つらそう