An Embedded Engineer’s Blog

とある組み込みエンジニアの備忘録的なブログです。

ClangNet(libclang)で構文解析サンプル - その1 (関数コールツリー、関数クロスリファレンス)

まえがき

今回はClangNet(libclangの.NET(C#) Binding)を使った実用的な構文解析のサンプルを作成してみます。
今回作成するサンプルでは、以下の2つの情報を出力する機能を実装します。

  1. 関数コールツリー
  2. 関数クロスリファレンス(宣言/定義/コール)情報

ClangNetのインストールや使用方法については過去記事をご参照ください。

an-embedded-engineer.hateblo.jp


解析対象ソース

まずは、今回解析対象にするC/C++ソースコードを用意します。
今回は以下の種類の関数を解析対象にします。

解析対象にするソースのサンプルを以下に示します。
解析対象ソースの詳しい説明は割愛します。


[TestClass.h]

#pragma once

namespace root
{
    namespace sub1
    {
        namespace sub2
        {
            class TestClass
            {
            public:
                TestClass();
                ~TestClass();

                void PublicMethod(int a, int b);

                int GetA() { return this->m_a; }
                int GetB() { return this->m_b; }
                int GetResult() { return this->m_result; }

                void SetA(int a) { this->m_a = a; }
                void SetB(int b) { this->m_b = b; }
                void SetResult(int result) { this->m_result = result; }

            private:
                int PrivateMethod(int a, int b);

            private:
                int m_a;
                int m_b;
                int m_result;
            };
        }
    }
}


[TestClass.cpp]

#include "TestClass.h"

namespace root
{
    namespace sub1
    {
        namespace sub2
        {
            TestClass::TestClass()
                : m_a(0)
                , m_b(0)
                , m_result(0)
            {
            }

            TestClass::~TestClass()
            {

            }

            void TestClass::PublicMethod(int a, int b)
            {
                int result = this->PrivateMethod(a, b);

                this->SetResult(result);
            }

            int TestClass::PrivateMethod(int a, int b)
            {
                this->SetA(a);
                this->SetB(b);

                return this->m_a + this->m_b;
            }
        }
    }
}


#include <iostream>

#include "TestClass.h"

void RecursiveCallTest(int i);

int main()
{
    root::sub1::sub2::TestClass test_instance;
    int a = 10;
    int b = 5;
    int result = 0;

    test_instance.SetA(a);

    test_instance.SetB(b);

    std::cout << "a : " << test_instance.GetA() << std::endl;

    std::cout << "b : " << test_instance.GetB() << std::endl;

    test_instance.PublicMethod(a, b);

    result = test_instance.GetResult();

    std::cout << "result : " << result << std::endl;

    RecursiveCallTest(10);
}

void RecursiveCallTest(int i)
{
    if (i < 0)
    {
        return;
    }
    else
    {
        std::cout << "i : " << i << std::endl;

        RecursiveCallTest(i - 1);
    }
}


構文解析サンプル

上記解析対象ソースに対して、関数コールツリー(Invokation Tree)と関数クロスリファレンス(Cross Reference)情報を生成して出力するサンプルを実装します。


実現方法

実現方法を簡単に説明します。
大きく分けると次の3ステップで実現します。

  1. Translation Unit(ソースファイル)解析
  2. Invokation Tree(関数コールツリー)生成
  3. Cross Reference(関数参照情報)生成

1.のTranslation Unit(ソースファイル)解析では、ClangNetの構文解析機能を使って各ソースファイルのAST(抽象構文木)を辿っていき、以下のような情報を収集します。

種別 意味 保持する情報 備考
Translation Unit情報 変換対象(ソースファイル)ごとに保持する情報 ソースファイルパス、宣言・定義されているBehavior情報マップ
Behavior情報マップ Behavior(関数)の宣言・定義位置をキーにした、Behavior情報のハッシュマップ Key : 関数宣言・定義位置、Value : Behavior情報
Behavior情報 種別(宣言/定義)、関数宣言、名前空間、ソース位置、呼び出している関数のInvokation情報リスト
Invokation情報 呼び出している関数名、ソース位置、参照している関数宣言のBehavior情報
  1. のInvokation Tree(関数コールツリー)生成では、1.で収集した情報をもとに、関数定義内で呼び出している関数を再帰的に辿っていきます。

  2. の Cross Reference(関数参照情報)生成では、1.で収集した情報をもとに、関数ごとに宣言箇所、定義箇所、呼び出し箇所のテーブルを生成します。


1. Translation Unit(ソースファイル)解析

ソースファイルからClangNetを使ってASTを解析し、必要な情報を収集します。

1-1. データ構造定義

まずは収集して構築するデータ構造を定義して行きます。

型名 ベース型 意味 保持する情報 備考
AstNodeInfo - ASTノードベース型 ソース位置情報
TranslationUnitInfo AstNodeInfo 変換対象ソース情報 ファイルパス、BehaviorLocationMap
BehaviorInfo AstNodeInfo 関数ノードベース型 種別(宣言/定義)、関数名、パラメータ、名前空間、定義、ID、InvokationInfoリスト
ClassBehaviorInfo BehaviorInfo クラスに所属する関数ノードベース型 クラス名
ConstructorInfo ClassBehaviorInfo コンストラクタ情報 -
DestructorInfo ClassBehaviorInfo デストラクタ情報 -
CppMethodInfo ClassBehaviorInfo クラスメンバ関数情報 返り値
FunctionInfo BehaviorInfo Cスタイル関数情報 返り値
InvokationInfo AstNodeInfo 関数呼び出し情報 関数名、ID、参照先関数BehaviorInfo
BehaviorLocationMap Dictionary<string,BehaviorInfo> ソース位置をキーとしたBehaviorInfoのDictionary Key : ソース位置 / Value : BehaviorInfo
TranslationUnitMap Dictionary<string,TranslationUnitInfo> ソースパスをキーとしたTranslationUnitInfoのDictionary Key : ソースパス / Value : TranslationUnitInfo

それぞれのソースを示します。

[AstNodeInfo.cs]

public abstract class AstNodeInfo
{
    public string Location { get; }

    public AstNodeInfo(ClangCursor cursor)
    {
        /* ソース上の位置情報を取得 */
        this.Location = this.GetLocation(cursor);
    }

    public string GetLocation(ClangCursor cursor)
    {
        /* カーソル位置を取得 */
        var loc = cursor.Location;

        /* カーソル位置がnullでない */
        if (loc != null)
        {
            /* ファイル位置を取得 */
            var floc = loc.FileLocation;

            /* ファイル位置がnullでない */
            if (floc != null)
            {
                /* ファイル情報がnullでない */
                if(floc.File != null)
                {
                    /* ファイルパスを取得 */
                    var path = Path.GetFullPath(floc.File.FileName);

                    /* ファイル上の行番号を取得 */
                    var line = floc.Line;

                    /* ファイル上の列番号を取得 */
                    var col = floc.Column;

                    /* 位置情報文字列を生成 */
                    var location = $"{path}[L.{line},C.{col}]";

                    return location;
                }
                else
                {
                    /* ファイル上の行番号を取得 */
                    var line = floc.Line;

                    /* ファイル上の列番号を取得 */
                    var col = floc.Column;

                    /* 位置情報文字列を生成 */
                    var location = $"[L.{line},C.{col}]";

                    return location;
                }
            }
            else
            {
                /* 位置情報なし */
                return string.Empty;
            }
        }
        else
        {
            /* 位置情報なし */
            return string.Empty;
        }
    }
}


[TranslationUnitInfo.cs]

public class TranslationUnitInfo : AstNodeInfo
{
    public string Path { get; }

    public BehaviorLocationMap BehaviorMap { get; } = new BehaviorLocationMap();

    public TranslationUnitInfo(ClangCursor cursor) : base(cursor)
    {
        /* ソースファイルのフルパスを取得 */
        this.Path = System.IO.Path.GetFullPath(cursor.Spelling);
    }

    public void AddBehavior(BehaviorInfo behavior)
    {
        this.BehaviorMap.Add(behavior);
    }

    public override string ToString()
    {
        return $"[TranslationUnit]{this.Path}";
    }
}


[BehaviorInfo.cs]

public abstract class BehaviorInfo : AstNodeInfo
{
    public string Type { get; }

    public string Name { get; }

    public string Parameters { get; }

    public string NameSpace { get; }

    /* 関数定義は継承先で生成 */
    public string Definition => this.GetDefinition();

    /* 関数のユニークなIDを生成 */
    public string ID => this.GenerateBehaviorID();

    public List<InvokationInfo> Invokations { get; } = new List<InvokationInfo>();

    protected abstract string GetDefinition();

    public BehaviorInfo(ClangCursor cursor) : base(cursor)
    {
        /* 関数の種別を判定 */
        this.Type = cursor.IsDefinition ? "Definition" : "Declaration";

        /* 関数名を取得 */
        this.Name = cursor.Spelling;

        /* 関数パラメータを取得 */
        this.Parameters = cursor.DisplayName.Replace(cursor.Spelling, "");

        /* 名前空間を取得 */
        this.NameSpace = this.GetNamespace(cursor);
    }

    public void AddInvokation(InvokationInfo invokation_info)
    {
        this.Invokations.Add(invokation_info);
    }

    private string GenerateBehaviorID()
    {
        /* 関数のIDには関数定義を使用 */
        return this.Definition;
    }

    /* 名前空間を取得 */
    private string GetNamespace(ClangCursor cursor)
    {
        /* 名前空間保持用スタック */
        var namespace_stack = new Stack<string>();

        /* 名前空間を再帰的にパース */
        this.ParseNamespace(cursor, namespace_stack);

        /* スタックの要素数が0 */
        if (namespace_stack.Count == 0)
        {
            /* 名前空間なし */
            return string.Empty;
        }
        else
        {
            var sb = new StringBuilder();

            /* 一番上の名前空間を追加 */
            sb.Append(namespace_stack.Pop());

            /* スタックが空になるまで */
            while(namespace_stack.Count != 0)
            {
                /* 階層化された名前空間を連結 */
                sb.Append("::");
                sb.Append(namespace_stack.Pop());
            }

            return sb.ToString();
        }
    }

    /* 名前空間を再帰的にパース */
    private void ParseNamespace(ClangCursor cursor, Stack<string> namespace_stack)
    {
         /* 関数の意味的な親を示すカーソルを取得 */
        var parent = cursor.SemanticParent;

        /* 親カーソルがnullでない */
        if (parent != null)
        {
            /* 親カーソルがTranslation Unit */
            if (parent.Kind == CursorKind.TranslationUnit)
            {
                /* 最上位のためパース終了 */
            }
            /* 親カーソルがNamespace */
            else if(parent.Kind == CursorKind.Namespace)
            {
                /* 名前空間保持用スタックに追加 */
                namespace_stack.Push(parent.Spelling);

                /* 親カーソルの名前空間を再帰的にパース */
                this.ParseNamespace(parent, namespace_stack);
            }
            /* それ以外(クラスなど) */
            else
            {
                /* 親カーソルの名前空間を再帰的にパース */
                this.ParseNamespace(parent, namespace_stack);
            }
        }
    }

    public override string ToString()
    {
        return $"[Behavior][{this.Type}] {this.Definition}";
    }
}


[ClassBehaviorInfo.cs]

public abstract class ClassBehaviorInfo : BehaviorInfo
{
    public string ClassName { get; }

    public ClassBehaviorInfo(ClangCursor cursor) : base(cursor)
    {
        /* 意味的な親カーソル(クラス)を取得 */
        var parent_class = cursor.SemanticParent;

        /* クラス名を取得 */
        this.ClassName = parent_class.Spelling;
    }

    public override string ToString()
    {
        return $"[ClassBehavior][{this.Type}] {this.Definition}";
    }
}


[ConstructorInfo.cs]

public class ConstructorInfo : ClassBehaviorInfo
{
    public ConstructorInfo(ClangCursor cursor) : base(cursor)
    {
    }

    protected override string GetDefinition()
    {
        var ns = this.NameSpace == string.Empty ? "" : $"{this.NameSpace}::";

        /* コンストラクタ定義 : [<名前空間>::]<クラス名>::<コンストラクタ名>(<パラメータ>) */
        return $"{ns}{this.ClassName}::{this.Name}{this.Parameters}";
    }

    public override string ToString()
    {
        return $"[Constructor][{this.Type}] {this.Definition}";
    }
}


[DestructorInfo.cs]

public class DestructorInfo : ClassBehaviorInfo
{
    public DestructorInfo(ClangCursor cursor) : base(cursor)
    {
    }

    protected override string GetDefinition()
    {
        var ns = this.NameSpace == string.Empty ? "" : $"{this.NameSpace}::";

        /* デストラクタ定義 : [<名前空間>::]<クラス名>::<デストラクタ名>(<パラメータ>) */
        return $"{ns}{this.ClassName}::{this.Name}{this.Parameters}";
    }

    public override string ToString()
    {
        return $"[Destructor][{this.Type}] {this.Definition}";
    }
}


[CppMethodInfo.cs]

public class CppMethodInfo : ClassBehaviorInfo
{
    public string ReturnType { get; }

    public CppMethodInfo(ClangCursor cursor) : base(cursor)
    {
        var rtype = cursor.ResultType;

        this.ReturnType = rtype.Spelling;
    }

    protected override string GetDefinition()
    {
        var ns = this.NameSpace == string.Empty ? "" : $"{this.NameSpace}::";

        /* C++クラスメンバ関数定義 : <返り値型> [<名前空間>::]<クラス名>::<関数名>(<パラメータ>) */
        return $"{this.ReturnType} {ns}{this.ClassName}::{this.Name}{this.Parameters}";
    }

    public override string ToString()
    {
        return $"[Function][{this.Type}] {this.Definition}";
    }
}


[FunctionInfo.cs]

public class FunctionInfo : BehaviorInfo
{
    public string ReturnType { get; }

    public FunctionInfo(ClangCursor cursor) : base(cursor)
    {
        var rtype = cursor.ResultType;

        this.ReturnType = rtype.Spelling;
    }

    protected override string GetDefinition()
    {
        var ns = this.NameSpace == string.Empty ? "" : $"{this.NameSpace}::";

        /* Cスタイル関数定義 : <返り値型> [<名前空間>::]<関数名>(<パラメータ>) */
        return $"{this.ReturnType} {ns}{this.Name}{this.Parameters}";
    }

    public override string ToString()
    {
        return $"[Function][{this.Type}] {this.Definition}";
    }
}


[InvokationInfo.cs]

public class InvokationInfo : AstNodeInfo
{
    public string ID { get; }

    public string Definition { get; }

    public string Name { get; }

    public BehaviorInfo Declaration { get; }

    public InvokationInfo(ClangCursor cursor) : base(cursor)
    {
        /* コール関数名を取得 */
        this.Name = cursor.Spelling;

        /* コール関数の参照先がnullでない */
        if (cursor.Referenced != null)
        {
            /* 参照先カーソルから関数宣言のBehavior Infoを生成 */
            this.Declaration = BehaviorInfoFactory.Create(cursor.Referenced);

            /* 関数宣言のBehavior Infoから関数IDを取得 */
            this.ID = this.Declaration.ID;

            /* 関数宣言のBehavior Infoから関数定義を取得 */
            this.Definition = this.Declaration.Definition;
        }
        else
        {
            throw new FieldAccessException($"Behavior Declaration Not Found : {this.Name}");
        }
    }

    public override string ToString()
    {
        return $"[Invokation]{this.Name}";
    }
}


[BehaviorlocationMap.cs]

public class BehaviorLocationMap : Dictionary<string, BehaviorInfo>
{
    public void Add(BehaviorInfo behavior)
    {
        if (this.ContainsKey(behavior.Location) == false)
        {
            this.Add(behavior.Location, behavior);
        }
        else
        {
            throw new InvalidOperationException($"Behavior Already Registered : {behavior.ID}");
        }
    }

    public void AddIfNotRegistered(BehaviorInfo behavior)
    {
        if (this.ContainsKey(behavior.Location) == false)
        {
            this.Add(behavior.Location, behavior);
        }
        else
        {
            /* Nothing to do */
        }
    }
}


[BehaviorlocationMap.cs]

public class TranslationUnitMap : Dictionary<string, TranslationUnitInfo>
{
    public void Add(TranslationUnitInfo tu)
    {
        if (this.ContainsKey(tu.Path) == false)
        {
            this.Add(tu.Path, tu);
        }
        else
        {
            throw new InvalidOperationException($"Translation Unit Already Registered : {tu.Path}");
        }
    }
}


1-2. AST解析

続いて、ASTを解析してTranslationUnitMapを生成するロジックを実現するクラスを定義します。
また、ASTを解析する部分は、様々なソースファイルの入力に対応できるように設定情報を保持するクラスを定義し、それをパラメータとして渡すようにします。
さらに、関数情報(BehaviorInfo)はカーソルの種別に対応したクラスのインスタンスを生成できるようにFactoryクラスを定義します。 それぞれのソースを以下に示します。

[TranslationUnitParseSetting.cs]

public class TranslationUnitParseSetting
{
    public List<string> Sources { get; set; } = new List<string>();

    public List<string> CommandLineArgs { get; set; } = new List<string>();

    public TranslationUnitFlags ParseOptions { get; set; } = TranslationUnitFlags.None;

    public bool DisplayDiag { get; set; } = true;

    public bool DumpAST { get; set; } = true;
}


[BehaviorInfoFactory.cs]

public static class BehaviorInfoFactory
{
    public static BehaviorInfo Create(ClangCursor cursor)
    {
        switch(cursor.Kind)
        {
            /* コンストラクタ */
            case CursorKind.Constructor:
                return new ConstructorInfo(cursor);
            /* デストラクタ */
            case CursorKind.Destructor:
                return new DestructorInfo(cursor);
            /* Cスタイル関数 */
            case CursorKind.FunctionDeclaration:
                return new FunctionInfo(cursor);
            /* C++クラスメンバ関数 */
            case CursorKind.CXXMethod:
                return new CppMethodInfo(cursor);
            default:
                throw new ArgumentException($"Not Behavior Cursor");
        }
    }
}


[TranslationUnitsParser.cs]

public static class TranslationUnitsParser
{
    public static TranslationUnitMap Parse(TranslationUnitParseSetting setting)
    {
        var impl = new TranslationUnitsParserImpl();

        var map = impl.Execute(setting);

        return map;
    }

    public class TranslationUnitsParserImpl : AMessageable
    {
        private TranslationUnitParseSetting Setting { get; set; }

        private TranslationUnitInfo CurrentTranslationUnit { get; set; }

        private BehaviorInfo CurrentBehavior { get; set; }

        private TranslationUnitMap TranslationUnitMap { get; set; } = new TranslationUnitMap();

        public TranslationUnitMap Execute(TranslationUnitParseSetting setting)
        {
            this.Setting = setting;

            var display_diag = setting.DisplayDiag;

            var src_path_list = setting.Sources;

            var command_line_args = setting.CommandLineArgs.ToArray();

            var options = setting.ParseOptions;

            /* Indexを生成 */
            using (var index = Clang.CreateIndex(false, display_diag))
            {
                /* 解析対象のソースファイルパスリストを走査 */
                foreach (var src_path in src_path_list)
                {
                    /* ソースファイルパスのファイルが存在するかを確認 */
                    if (File.Exists(src_path))
                    {
                        /* ソースファイルをパースし、Translation Unitを生成 */
                        using (var tu = index.ParseTranslationUnit(src_path, command_line_args, new ClangUnsavedFile[0], options))
                        {
                            /* 解析実行 */
                            this.ExecuteCore(index, tu);
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException($"Source File Not Found :{src_path}");
                    }
                }
            }

            return this.TranslationUnitMap;
        }

        private void ExecuteCore(ClangIndex index, ClangTranslationUnit tu)
        {
            if (this.Setting.DumpAST)
            {
                this.SendMessage($"AST Dump:");
            }

            /* ASTを再帰的にパース */
            this.VisitChild(tu.Cursor, 0);
        }

        private ChildVisitResult Visitor(ClangCursor cursor, ClangCursor parent, int depth)
        {
            /* カーソル位置を取得 */
            var loc = cursor.Location;

            /* 解析対象ソースファイル内 */
            if (loc.IsFromMainFile == true)
            {
                /* 子要素をパース */
                this.VisitChild(cursor, depth);
            }
            else
            {
                /* システムヘッダ内 */
                if (loc.IsInSystemHeader == true)
                {
                    /* Nothing to do */
                }
                /* ユーザヘッダ内 */
                else
                {
                    /* 子要素をパース */
                    this.VisitChild(cursor, depth);
                }
            }

            return ChildVisitResult.Continue;
        }

        private void VisitChild(ClangCursor cursor, int depth)
        {
            /* デバッグ用にASTをダンプ */
            if (this.Setting.DumpAST)
            {
                this.DumpAstInfo(cursor, depth);
            }

            /* 関数コール情報を解析 */
            this.AnalyseInvokationInfo(cursor);

            cursor.VisitChildren(this.Visitor, depth + 1);
        }

        private void DumpAstInfo(ClangCursor cursor, int depth)
        {
            var indent = new string(' ', depth * 2);

            var kind = cursor.Kind;

            var name = cursor.Spelling;

            var loc = cursor.Location.ToStringEx();

            this.SendMessage($"{indent}[{kind}] {name} @ {loc}");
        }

        private void AnalyseInvokationInfo(ClangCursor cursor)
        {
            switch (cursor.Kind)
            {
                /* 解析対象ソースファイル */
                case CursorKind.TranslationUnit:
                    /* Translation Unit Infoを生成し、Translation Unit Mapに追加 */
                    this.CurrentTranslationUnit = new TranslationUnitInfo(cursor);
                    this.TranslationUnitMap.Add(this.CurrentTranslationUnit);
                    break;
                /* コンストラクタ */
                case CursorKind.Constructor:
                    /* Constructor Infoを生成し、Translation Unit Infoに追加 */
                    this.CurrentBehavior = BehaviorInfoFactory.Create(cursor);
                    this.CurrentTranslationUnit.AddBehavior(this.CurrentBehavior);
                    break;
                /* デストラクタ */
                case CursorKind.Destructor:
                    /* Destructor Infoを生成し、Translation Unit Infoに追加 */
                    this.CurrentBehavior = BehaviorInfoFactory.Create(cursor);
                    this.CurrentTranslationUnit.AddBehavior(this.CurrentBehavior);
                    break;
                /* Cスタイル関数*/
                case CursorKind.FunctionDeclaration:
                    /* Function Infoを生成し、Translation Unit Infoに追加 */
                    this.CurrentBehavior = BehaviorInfoFactory.Create(cursor);
                    this.CurrentTranslationUnit.AddBehavior(this.CurrentBehavior);
                    break;
                /* C++メンバ関数 */
                case CursorKind.CXXMethod:
                    /* Cpp Method Infoを生成し、Translation Unit Infoに追加 */
                    this.CurrentBehavior = BehaviorInfoFactory.Create(cursor);
                    this.CurrentTranslationUnit.AddBehavior(this.CurrentBehavior);
                    break;
                /* 関数コール */
                case CursorKind.CallExpression:
                    /* Invokation Infoを生成し、Translation Unit Infoに追加 */
                    var invokation_info = new InvokationInfo(cursor);
                    this.CurrentBehavior.AddInvokation(invokation_info);
                    break;
                default:
                    break;
            }
        }
    }
}


2. Invokation Tree(関数コールツリー)生成

次は、1.で生成したTranslationUnitMapの情報から関数コールツリー情報を生成し、出力する機能を持つクラスを実装します。
関数コールツリーを出力するために、TranslationUnitMapに登録されている各TranslationUnitInfoのBehaviorMapから、種別がDefinition(関数定義)のBehaviorInfoを抽出し、Behavior IDをキーとしたハッシュマップを生成します。 生成するハッシュマップ(BehaviorDefinitionMapクラス)の定義と、ハッシュマップ生成処理の実装コードを以下に示します。

[BehaviorDefinitionMap.cs]

public class BehaviorDefinitionMap : Dictionary<string, BehaviorInfo>
{
    public void Add(BehaviorInfo behavior)
    {
        if (this.ContainsKey(behavior.ID) == false)
        {
            this.Add(behavior.ID, behavior);
        }
        else
        {
            throw new InvalidOperationException($"Behavior Already Registered : {behavior.ID}");
        }
    }

    public void AddIfNotRegistered(BehaviorInfo behavior)
    {
        if (this.ContainsKey(behavior.ID) == false)
        {
            this.Add(behavior.ID, behavior);
        }
        else
        {
            /* Nothing to do */
        }
    }
}


[ハッシュマップ生成処理]

private void CreateBehaviorDefinitionMap()
{
    /* Translation Unit Infoを走査 */
    foreach (var tu in this.TranslationUnitMap.Values)
    {
        /* Translation Unit InfoのBehavior MapからTypeがDefinitionのBehavior Infoを抽出 */
        var behavior_definitions = tu.BehaviorMap.Values.Where(b => b.Type == "Definition").ToList();

        /* 抽出したBehavior Infoを走査 */
        foreach (var behavior_definition in behavior_definitions)
        {
            /* Behavior Infoが登録されていなければ、Behavior Definition Mapに登録 */
            this.BehaviorDefinitionMap.AddIfNotRegistered(behavior_definition);
        }
    }
}


生成したBehaviorDefinitionMapを用いて、関数が呼び出している関数情報を辿っていき、関数のコールツリーを出力します。
実装コードを以下に示します。

[関数コールツリー出力]

private void DumpInvokationTrees()
{
    /* Behavior Definition MapのValue(BehaviorInfo)リストを取得 */
    var behavior_defnitions = this.BehaviorDefinitionMap.Values;

    /* Behavior Definitionリストを走査 */
    foreach (var behavior in behavior_defnitions)
    {
        /* 関数定義と定義位置を出力 */
        this.SendMessage($"{behavior.Definition} @ {behavior.Location}");

        /* 関数のコールツリーを出力 */
        this.DumpInvokationTree(behavior);

        /* 改行 */
        this.SendMessage();
    }
}

private void DumpInvokationTree(BehaviorInfo behavior, int depth = 1)
{
    /* Behavior InfoのInvokation Infoリストを走査 */
    foreach (var invokation in behavior.Invokations)
    {
        /* インデント(1段階)を生成 */
        var indent = new string(' ', depth * 2);

        /* インデント(2段階)を生成 */
        var indent2 = new string(' ', (depth + 1) * 2);

        /* コール関数定義とコール位置を出力 */
        this.SendMessage($"{indent}{invokation.Definition} @ {invokation.Location}");

        /* 親関数とコール関数のIDが同一(再帰呼び出し) */
        if (behavior.ID == invokation.ID)
        {
            /* 再帰呼び出しの旨を出力 */
            this.SendMessage($"{indent2} <Recursive Call...>");
        }
        else
        {
            /* Behavior Definition Mapにコール関数と同一IDのBehavior Infoがあるかを確認 */
            if (this.BehaviorDefinitionMap.ContainsKey(invokation.ID))
            {
                /* コール関数のBehavior Infoを取得 */
                var child_behavior = this.BehaviorDefinitionMap[invokation.ID];

                /* コール関数を親にしてコールツリーを出力 */
                this.DumpInvokationTree(child_behavior, depth + 1);
            }
            else
            {
                /* コール関数の関数定義が見つからない旨を出力 */
                this.SendMessage($"{indent2} <Behavior Definition Not Found...>");
            }
        }
    }
}


ソース全文を以下に示します。

[InvokationTreeDumper.cs]

public static class InvokationTreeDumper
{
    public static void Dump(TranslationUnitMap map)
    {
        var impl = new InvokationTreeDumperImpl();

        impl.Execute(map);
    }

    public class InvokationTreeDumperImpl : AMessageable
    {
        private TranslationUnitMap TranslationUnitMap { get; set; } = new TranslationUnitMap();

        private BehaviorDefinitionMap BehaviorDefinitionMap { get; set; } = new BehaviorDefinitionMap();

        public void Execute(TranslationUnitMap map)
        {
            this.TranslationUnitMap = map;

            this.CreateBehaviorDefinitionMap();

            this.SendMessage();
            this.SendMessage("--------------------------------------");
            this.SendMessage("Invokation Tree:");

            this.DumpInvokationTrees();
        }

        private void CreateBehaviorDefinitionMap()
        {
            foreach (var tu in this.TranslationUnitMap.Values)
            {
                var behavior_definitions = tu.BehaviorMap.Values.Where(b => b.Type == "Definition").ToList();

                foreach (var behavior_definition in behavior_definitions)
                {
                    this.BehaviorDefinitionMap.AddIfNotRegistered(behavior_definition);
                }
            }
        }

        private void DumpInvokationTrees()
        {
            var behavior_defnitions = this.BehaviorDefinitionMap.Values;

            foreach (var behavior in behavior_defnitions)
            {
                this.SendMessage($"{behavior.Definition} @ {behavior.Location}");

                this.DumpInvokationTree(behavior);

                this.SendMessage();
            }
        }

        private void DumpInvokationTree(BehaviorInfo behavior, int depth = 1)
        {
            foreach (var invokation in behavior.Invokations)
            {
                var indent = new string(' ', depth * 2);

                var indent2 = new string(' ', (depth + 1) * 2);

                this.SendMessage($"{indent}{invokation.Definition} @ {invokation.Location}");

                if (behavior.ID == invokation.ID)
                {
                    this.SendMessage($"{indent2} <Recursive Call...>");
                }
                else
                {
                    if (this.BehaviorDefinitionMap.ContainsKey(invokation.ID))
                    {
                        var child_behavior = this.BehaviorDefinitionMap[invokation.ID];

                        this.DumpInvokationTree(child_behavior, depth + 1);
                    }
                    else
                    {
                        this.SendMessage($"{indent2} <Behavior Definition Not Found...>");
                    }
                }
            }
        }
    }
}


3. Cross Reference(関数参照情報)生成

続いて1.で生成したTranslationUnitMapの情報から関数クロスリファレンス(宣言/定義/コール)情報を生成し、出力する機能を持つクラスを実装します。
クロスリファレンス情報を保持するためのクラス(BehaviorCrossReferenceInfo)と、それを関数ごとにまとめるコンテナ(BehaviorCrossReferenceMap)を定義します。
なお、BehaviorCrossReferenceInfoは、重複を排除するためにValueObjectパターンで実装します。
それぞれの実装コードを以下に示します。

[ValueObject.cs]

public abstract class ValueObject<T> where T : ValueObject<T>
{
    /* データの同一性を判定する関数 -> 継承先で実装 */
    protected abstract bool EqualsCore(T other);

    /* ハッシュコード生成に使用するパラメータを取得する関数 -> 継承先で実装 */
    protected abstract IEnumerable<object> GetHashCodeParameters();

    public static bool operator ==(ValueObject<T> left, ValueObject<T> right)
    {
        return Equals(left, right);
    }

    public static bool operator !=(ValueObject<T> left, ValueObject<T> right)
    {
        return !Equals(left, right);
    }

    public override bool Equals(object obj)
    {
        var other = obj as T;

        if (other == null)
        {
            return false;
        }
        else
        {
            return this.EqualsCore(other);
        }
    }

    public override int GetHashCode()
    {
        var objs = this.GetHashCodeParameters();

        var hash = 0;

        foreach(var obj in objs)
        {
            hash ^= obj.GetHashCode();
        }

        return hash;
    }
}

[BehaviorCrossReferenceInfo.cs]

public class BehaviorCrossReferenceInfo : ValueObject<BehaviorCrossReferenceInfo>
{
    public string ID { get; }

    public string Type { get; }

    public string Definition { get; }

    public string Location { get; }

    /* Behavior Infoからクロスリファレンス情報を生成 */
    public BehaviorCrossReferenceInfo(BehaviorInfo info)
    {
        this.ID = info.ID;

        this.Type = info.Type;

        this.Definition = info.Definition;

        this.Location = info.Location;
    }

    /* Invokation Infoからクロスリファレンス情報を生成 */
    public BehaviorCrossReferenceInfo(InvokationInfo info)
    {
        this.ID = info.ID;

        this.Type = "Call";

        this.Definition = info.Definition;

        this.Location = info.Location;
    }

    public override string ToString()
    {
        return $"[{this.Type}]{this.Definition} @ {this.Location}";
    }

    /* 各プロパティの値が全て一致する場合、同一のオブジェクトとみなす */
    protected override bool EqualsCore(BehaviorCrossReferenceInfo other)
    {
        return (this.ID == other.ID && this.Type == other.Type && this.Definition == other.Definition && this.Location == other.Location);
    }

    /* 各プロパティの値をもとにハッシュコードを生成 */
    protected override IEnumerable<object> GetHashCodeParameters()
    {
        var objs = new List<object>()
        {
            this.ID,
            this.Definition,
            this.Type,
            this.Location,
        };

        return objs;
    }
}


[BehaviorCrossReferenceMap.cs]

/* 同一ID(同一関数)ごとにクロスリファレンス情報をまとめるコンテナ(重複は排除) */
public class BehaviorCrossReferenceMap : Dictionary<string, HashSet<BehaviorCrossReferenceInfo>>
{
    public void Add(BehaviorCrossReferenceInfo info)
    {
        if (this.ContainsKey(info.ID) == false)
        {
            this.Add(info.ID, new HashSet<BehaviorCrossReferenceInfo>());
        }

        this[info.ID].Add(info);
    }
}


上記で示したクロスリファレンス情報を生成するコードを実装します。

[クロスリファレンス情報生成]

private void AnalyseCrossReferences()
{
    var map1 = new BehaviorCrossReferenceMap();

    /* Translation Unit Infoを走査 */
    foreach (var tu in this.TranslationUnitMap.Values)
    {
        /* Translation Unit InfoのBehavior Mapの値(Behavior Info)を走査 */
        foreach (var behavior in tu.BehaviorMap.Values)
        {
            /* Behavior InfoからBehavior Cross Reference Infoを生成 */
            var xref_behavior = new BehaviorCrossReferenceInfo(behavior);

            /* Cross Reference Mapに登録 */
            map1.Add(xref_behavior);

            /* Behavior InfoのInvokation Infoリストを走査 */
            foreach (var invoke in behavior.Invokations)
            {
                /* Invokation InfoからBehavior Cross Reference Infoを生成 */
                var xref_invoke = new BehaviorCrossReferenceInfo(invoke);

                /* Cross Reference Mapに登録 */
                map1.Add(xref_invoke);
            }
        }
    }

    /* 新規Behavior Cross Reference Mapを生成 */
    var map2 = new BehaviorCrossReferenceMap();

    /* 前段で生成したBehavior Cross Reference Mapの各キーを走査 */
    foreach (var key in map1.Keys)
    {
        /* キーを指定してハッシュセットを取得 */
        var set = map1[key];

        /* Behavior Cross Reference InfoのTypeでソート */
        var sorted_set = set.OrderBy(x => x.Type).ToHashSet();

        /* ソート後のハッシュセットを登録 */
        map2.Add(key, sorted_set);
    }

    this.CrossReferenceMap = map2;
}

次に、生成したクロスリファレンス情報を順番に出力します。
実装コードを以下に示します。


[クロスリファレンス情報出力]

private void DumpCrossReferences()
{
    /* Behavior Cross Reference Mapの各キー(Behavior ID)を走査 */
    foreach (var xref_id in this.CrossReferenceMap.Keys)
    {
        /* Behavior IDを出力 */
        this.SendMessage($"{xref_id}");

        /* キーを指定してBehavior Cross Reference Infoのハッシュセットを取得 */
        var xref_hash_set = this.CrossReferenceMap[xref_id];

        /* ハッシュセットの要素(Behavior Cross Reference Info)を走査 */
        foreach (var xref_info in xref_hash_set)
        {
            var type = xref_info.Type;
            var loc = xref_info.Location;
            this.SendMessage($"  [{type}] @ {loc}");
        }

        this.SendMessage();
    }
}


ソース全文を以下に示します。

[CrossReferencesDumper.cs]

public static class CrossReferencesDumper
{
    public static void Dump(TranslationUnitMap map)
    {
        var impl = new CrossReferencesDumperImpl();

        impl.Execute(map);
    }

    public class CrossReferencesDumperImpl : AMessageable
    {
        private TranslationUnitMap TranslationUnitMap { get; set; } = new TranslationUnitMap();

        private BehaviorCrossReferenceMap CrossReferenceMap { get; set; } = new BehaviorCrossReferenceMap();

        public void Execute(TranslationUnitMap map)
        {
            this.TranslationUnitMap = map;

            this.AnalyseCrossReferences();

            this.SendMessage();
            this.SendMessage("--------------------------------------");
            this.SendMessage("Cross References:");

            this.DumpCrossReferences();
        }

        private void AnalyseCrossReferences()
        {
            var map1 = new BehaviorCrossReferenceMap();

            foreach (var tu in this.TranslationUnitMap.Values)
            {
                foreach (var behavior in tu.BehaviorMap.Values)
                {
                    var xref_behavior = new BehaviorCrossReferenceInfo(behavior);

                    map1.Add(xref_behavior);

                    foreach (var invoke in behavior.Invokations)
                    {
                        var xref_invoke = new BehaviorCrossReferenceInfo(invoke);

                        map1.Add(xref_invoke);
                    }
                }
            }

            var map2 = new BehaviorCrossReferenceMap();

            foreach (var key in map1.Keys)
            {
                var set = map1[key];

                var sorted_set = set.OrderBy(x => x.Type).ToHashSet();

                map2.Add(key, sorted_set);
            }

            this.CrossReferenceMap = map2;
        }

        private void DumpCrossReferences()
        {
            foreach (var xref_id in this.CrossReferenceMap.Keys)
            {
                this.SendMessage($"{xref_id}");

                var xref_hash_set = this.CrossReferenceMap[xref_id];

                foreach (var xref_info in xref_hash_set)
                {
                    var type = xref_info.Type;
                    var loc = xref_info.Location;
                    this.SendMessage($"  [{type}] @ {loc}");
                }

                this.SendMessage();
            }
        }
    }
}


4. 各処理呼び出し

最後に、1-3.で実装した各機能を呼び出す処理を実装します。

public static class BehaviorRelationsAnalyser
{
    public static void Execute()
    {
        /* Translation Unitのパース設定情報を生成 */
        var setting = new TranslationUnitParseSetting()
        {
            /* パース対象ソースパスの指定 */
            Sources = new List<string>()
            {
                "./Code2/src/main.cpp",
                "./Code2/src/TestClass.cpp",
            },
            /* libclangに渡すコマンドライン引数(インクルードパスなど)の指定 */
            CommandLineArgs = new List<string>()
            {
                "-I./Code2/inc",
            },
            /* libclangのTranslation Unitパースオプション */
            ParseOptions = TranslationUnitFlags.None,
            /* ダイアグ情報(構文エラーなど)を表示 */
            DisplayDiag = true,
            /* AST情報を出力(デバッグ用) */
            DumpAST = true,
        };

        /* Translation Unitをパースし、Translation Unit Mapを生成 */
        var map = TranslationUnitsParser.Parse(setting);

        /* Translation Unit Mapから関数コールツリーを生成して出力 */
        InvokationTreeDumper.Dump(map);

        /* Translation Unit Mapから関数クロスリファレンス情報を生成して出力 */
        CrossReferencesDumper.Dump(map);
    }
}


解析対象サンプルソースを入力して実行した結果を示します。
上から、ASTのダンプ結果、関数コールツリー、関数クロスリファレンス情報が出力されています。

[実行結果]

AST Dump:
[TranslationUnit] ./Code2/src/main.cpp @ 
  [Namespace] root @ ./Code2/inc/TestClass.h[L.3,C.11]
    [Namespace] sub1 @ ./Code2/inc/TestClass.h[L.5,C.15]
      [Namespace] sub2 @ ./Code2/inc/TestClass.h[L.7,C.19]
        [ClassDeclaration] TestClass @ ./Code2/inc/TestClass.h[L.9,C.19]
          [LastDeclaration]  @ ./Code2/inc/TestClass.h[L.11,C.13]
          [Constructor] TestClass @ ./Code2/inc/TestClass.h[L.12,C.17]
          [Destructor] ~TestClass @ ./Code2/inc/TestClass.h[L.13,C.17]
          [CXXMethod] PublicMethod @ ./Code2/inc/TestClass.h[L.15,C.22]
            [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.15,C.39]
            [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.15,C.46]
          [CXXMethod] GetA @ ./Code2/inc/TestClass.h[L.17,C.21]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.17,C.28]
              [ReturnStatement]  @ ./Code2/inc/TestClass.h[L.17,C.30]
                [UnexposedExpression] m_a @ ./Code2/inc/TestClass.h[L.17,C.43]
                  [MemberReferenceExpression] m_a @ ./Code2/inc/TestClass.h[L.17,C.43]
                    [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.17,C.37]
          [CXXMethod] GetB @ ./Code2/inc/TestClass.h[L.18,C.21]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.18,C.28]
              [ReturnStatement]  @ ./Code2/inc/TestClass.h[L.18,C.30]
                [UnexposedExpression] m_b @ ./Code2/inc/TestClass.h[L.18,C.43]
                  [MemberReferenceExpression] m_b @ ./Code2/inc/TestClass.h[L.18,C.43]
                    [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.18,C.37]
          [CXXMethod] GetResult @ ./Code2/inc/TestClass.h[L.19,C.21]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.19,C.33]
              [ReturnStatement]  @ ./Code2/inc/TestClass.h[L.19,C.35]
                [UnexposedExpression] m_result @ ./Code2/inc/TestClass.h[L.19,C.48]
                  [MemberReferenceExpression] m_result @ ./Code2/inc/TestClass.h[L.19,C.48]
                    [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.19,C.42]
          [CXXMethod] SetA @ ./Code2/inc/TestClass.h[L.21,C.22]
            [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.21,C.31]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.21,C.34]
              [BinaryOperator]  @ ./Code2/inc/TestClass.h[L.21,C.36]
                [MemberReferenceExpression] m_a @ ./Code2/inc/TestClass.h[L.21,C.42]
                  [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.21,C.36]
                [UnexposedExpression] a @ ./Code2/inc/TestClass.h[L.21,C.48]
                  [DeclarationReferenceExpression] a @ ./Code2/inc/TestClass.h[L.21,C.48]
          [CXXMethod] SetB @ ./Code2/inc/TestClass.h[L.22,C.22]
            [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.22,C.31]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.22,C.34]
              [BinaryOperator]  @ ./Code2/inc/TestClass.h[L.22,C.36]
                [MemberReferenceExpression] m_b @ ./Code2/inc/TestClass.h[L.22,C.42]
                  [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.22,C.36]
                [UnexposedExpression] b @ ./Code2/inc/TestClass.h[L.22,C.48]
                  [DeclarationReferenceExpression] b @ ./Code2/inc/TestClass.h[L.22,C.48]
          [CXXMethod] SetResult @ ./Code2/inc/TestClass.h[L.23,C.22]
            [ParmDeclaration] result @ ./Code2/inc/TestClass.h[L.23,C.36]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.23,C.44]
              [BinaryOperator]  @ ./Code2/inc/TestClass.h[L.23,C.46]
                [MemberReferenceExpression] m_result @ ./Code2/inc/TestClass.h[L.23,C.52]
                  [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.23,C.46]
                [UnexposedExpression] result @ ./Code2/inc/TestClass.h[L.23,C.63]
                  [DeclarationReferenceExpression] result @ ./Code2/inc/TestClass.h[L.23,C.63]
          [LastDeclaration]  @ ./Code2/inc/TestClass.h[L.25,C.13]
          [CXXMethod] PrivateMethod @ ./Code2/inc/TestClass.h[L.26,C.21]
            [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.26,C.39]
            [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.26,C.46]
          [LastDeclaration]  @ ./Code2/inc/TestClass.h[L.28,C.13]
          [FieldDeclaration] m_a @ ./Code2/inc/TestClass.h[L.29,C.21]
          [FieldDeclaration] m_b @ ./Code2/inc/TestClass.h[L.30,C.21]
          [FieldDeclaration] m_result @ ./Code2/inc/TestClass.h[L.31,C.21]
  [FunctionDeclaration] RecursiveCallTest @ ./Code2/src/main.cpp[L.5,C.6]
    [ParmDeclaration] i @ ./Code2/src/main.cpp[L.5,C.28]
  [FunctionDeclaration] main @ ./Code2/src/main.cpp[L.7,C.5]
    [CompoundStatement]  @ ./Code2/src/main.cpp[L.8,C.1]
      [DeclarationStatement]  @ ./Code2/src/main.cpp[L.9,C.5]
        [VarDeclaration] test_instance @ ./Code2/src/main.cpp[L.9,C.33]
          [NamespaceReference] root @ ./Code2/src/main.cpp[L.9,C.5]
          [NamespaceReference] sub1 @ ./Code2/src/main.cpp[L.9,C.11]
          [NamespaceReference] sub2 @ ./Code2/src/main.cpp[L.9,C.17]
          [TypeReference] class root::sub1::sub2::TestClass @ ./Code2/src/main.cpp[L.9,C.23]
          [CallExpression] TestClass @ ./Code2/src/main.cpp[L.9,C.33]
      [DeclarationStatement]  @ ./Code2/src/main.cpp[L.10,C.5]
        [VarDeclaration] a @ ./Code2/src/main.cpp[L.10,C.9]
          [IntegerLiteral]  @ ./Code2/src/main.cpp[L.10,C.13]
      [DeclarationStatement]  @ ./Code2/src/main.cpp[L.11,C.5]
        [VarDeclaration] b @ ./Code2/src/main.cpp[L.11,C.9]
          [IntegerLiteral]  @ ./Code2/src/main.cpp[L.11,C.13]
      [DeclarationStatement]  @ ./Code2/src/main.cpp[L.12,C.5]
        [VarDeclaration] result @ ./Code2/src/main.cpp[L.12,C.9]
          [IntegerLiteral]  @ ./Code2/src/main.cpp[L.12,C.18]
      [CallExpression] SetA @ ./Code2/src/main.cpp[L.14,C.5]
        [MemberReferenceExpression] SetA @ ./Code2/src/main.cpp[L.14,C.19]
          [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.14,C.5]
        [UnexposedExpression] a @ ./Code2/src/main.cpp[L.14,C.24]
          [DeclarationReferenceExpression] a @ ./Code2/src/main.cpp[L.14,C.24]
      [CallExpression] SetB @ ./Code2/src/main.cpp[L.16,C.5]
        [MemberReferenceExpression] SetB @ ./Code2/src/main.cpp[L.16,C.19]
          [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.16,C.5]
        [UnexposedExpression] b @ ./Code2/src/main.cpp[L.16,C.24]
          [DeclarationReferenceExpression] b @ ./Code2/src/main.cpp[L.16,C.24]
      [CallExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.5]
        [CallExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.5]
          [CallExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.5]
            [DeclarationReferenceExpression] cout @ ./Code2/src/main.cpp[L.18,C.10]
              [NamespaceReference] std @ ./Code2/src/main.cpp[L.18,C.5]
            [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.15]
              [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.15]
            [UnexposedExpression]  @ ./Code2/src/main.cpp[L.18,C.18]
              [StringLiteral] "a : " @ ./Code2/src/main.cpp[L.18,C.18]
          [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.25]
            [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.25]
          [CallExpression] GetA @ ./Code2/src/main.cpp[L.18,C.28]
            [MemberReferenceExpression] GetA @ ./Code2/src/main.cpp[L.18,C.42]
              [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.18,C.28]
        [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.49]
          [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.18,C.49]
        [UnexposedExpression] endl @ ./Code2/src/main.cpp[L.18,C.57]
          [DeclarationReferenceExpression] endl @ ./Code2/src/main.cpp[L.18,C.57]
            [NamespaceReference] std @ ./Code2/src/main.cpp[L.18,C.52]
      [CallExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.5]
        [CallExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.5]
          [CallExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.5]
            [DeclarationReferenceExpression] cout @ ./Code2/src/main.cpp[L.20,C.10]
              [NamespaceReference] std @ ./Code2/src/main.cpp[L.20,C.5]
            [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.15]
              [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.15]
            [UnexposedExpression]  @ ./Code2/src/main.cpp[L.20,C.18]
              [StringLiteral] "b : " @ ./Code2/src/main.cpp[L.20,C.18]
          [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.25]
            [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.25]
          [CallExpression] GetB @ ./Code2/src/main.cpp[L.20,C.28]
            [MemberReferenceExpression] GetB @ ./Code2/src/main.cpp[L.20,C.42]
              [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.20,C.28]
        [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.49]
          [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.20,C.49]
        [UnexposedExpression] endl @ ./Code2/src/main.cpp[L.20,C.57]
          [DeclarationReferenceExpression] endl @ ./Code2/src/main.cpp[L.20,C.57]
            [NamespaceReference] std @ ./Code2/src/main.cpp[L.20,C.52]
      [CallExpression] PublicMethod @ ./Code2/src/main.cpp[L.22,C.5]
        [MemberReferenceExpression] PublicMethod @ ./Code2/src/main.cpp[L.22,C.19]
          [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.22,C.5]
        [UnexposedExpression] a @ ./Code2/src/main.cpp[L.22,C.32]
          [DeclarationReferenceExpression] a @ ./Code2/src/main.cpp[L.22,C.32]
        [UnexposedExpression] b @ ./Code2/src/main.cpp[L.22,C.35]
          [DeclarationReferenceExpression] b @ ./Code2/src/main.cpp[L.22,C.35]
      [BinaryOperator]  @ ./Code2/src/main.cpp[L.24,C.5]
        [DeclarationReferenceExpression] result @ ./Code2/src/main.cpp[L.24,C.5]
        [CallExpression] GetResult @ ./Code2/src/main.cpp[L.24,C.14]
          [MemberReferenceExpression] GetResult @ ./Code2/src/main.cpp[L.24,C.28]
            [DeclarationReferenceExpression] test_instance @ ./Code2/src/main.cpp[L.24,C.14]
      [CallExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.5]
        [CallExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.5]
          [CallExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.5]
            [DeclarationReferenceExpression] cout @ ./Code2/src/main.cpp[L.26,C.10]
              [NamespaceReference] std @ ./Code2/src/main.cpp[L.26,C.5]
            [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.15]
              [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.15]
            [UnexposedExpression]  @ ./Code2/src/main.cpp[L.26,C.18]
              [StringLiteral] "result : " @ ./Code2/src/main.cpp[L.26,C.18]
          [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.30]
            [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.30]
          [UnexposedExpression] result @ ./Code2/src/main.cpp[L.26,C.33]
            [DeclarationReferenceExpression] result @ ./Code2/src/main.cpp[L.26,C.33]
        [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.40]
          [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.26,C.40]
        [UnexposedExpression] endl @ ./Code2/src/main.cpp[L.26,C.48]
          [DeclarationReferenceExpression] endl @ ./Code2/src/main.cpp[L.26,C.48]
            [NamespaceReference] std @ ./Code2/src/main.cpp[L.26,C.43]
      [CallExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.28,C.5]
        [UnexposedExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.28,C.5]
          [DeclarationReferenceExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.28,C.5]
        [IntegerLiteral]  @ ./Code2/src/main.cpp[L.28,C.23]
  [FunctionDeclaration] RecursiveCallTest @ ./Code2/src/main.cpp[L.31,C.6]
    [ParmDeclaration] i @ ./Code2/src/main.cpp[L.31,C.28]
    [CompoundStatement]  @ ./Code2/src/main.cpp[L.32,C.1]
      [IfStatement]  @ ./Code2/src/main.cpp[L.33,C.5]
        [BinaryOperator]  @ ./Code2/src/main.cpp[L.33,C.9]
          [UnexposedExpression] i @ ./Code2/src/main.cpp[L.33,C.9]
            [DeclarationReferenceExpression] i @ ./Code2/src/main.cpp[L.33,C.9]
          [IntegerLiteral]  @ ./Code2/src/main.cpp[L.33,C.13]
        [CompoundStatement]  @ ./Code2/src/main.cpp[L.34,C.5]
          [ReturnStatement]  @ ./Code2/src/main.cpp[L.35,C.9]
        [CompoundStatement]  @ ./Code2/src/main.cpp[L.38,C.5]
          [CallExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.9]
            [CallExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.9]
              [CallExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.9]
                [DeclarationReferenceExpression] cout @ ./Code2/src/main.cpp[L.39,C.14]
                  [NamespaceReference] std @ ./Code2/src/main.cpp[L.39,C.9]
                [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.19]
                  [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.19]
                [UnexposedExpression]  @ ./Code2/src/main.cpp[L.39,C.22]
                  [StringLiteral] "i : " @ ./Code2/src/main.cpp[L.39,C.22]
              [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.29]
                [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.29]
              [UnexposedExpression] i @ ./Code2/src/main.cpp[L.39,C.32]
                [DeclarationReferenceExpression] i @ ./Code2/src/main.cpp[L.39,C.32]
            [UnexposedExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.34]
              [DeclarationReferenceExpression] operator<< @ ./Code2/src/main.cpp[L.39,C.34]
            [UnexposedExpression] endl @ ./Code2/src/main.cpp[L.39,C.42]
              [DeclarationReferenceExpression] endl @ ./Code2/src/main.cpp[L.39,C.42]
                [NamespaceReference] std @ ./Code2/src/main.cpp[L.39,C.37]
          [CallExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.41,C.9]
            [UnexposedExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.41,C.9]
              [DeclarationReferenceExpression] RecursiveCallTest @ ./Code2/src/main.cpp[L.41,C.9]
            [BinaryOperator]  @ ./Code2/src/main.cpp[L.41,C.27]
              [UnexposedExpression] i @ ./Code2/src/main.cpp[L.41,C.27]
                [DeclarationReferenceExpression] i @ ./Code2/src/main.cpp[L.41,C.27]
              [IntegerLiteral]  @ ./Code2/src/main.cpp[L.41,C.31]
AST Dump:
[TranslationUnit] ./Code2/src/TestClass.cpp @ 
  [Namespace] root @ ./Code2/inc/TestClass.h[L.3,C.11]
    [Namespace] sub1 @ ./Code2/inc/TestClass.h[L.5,C.15]
      [Namespace] sub2 @ ./Code2/inc/TestClass.h[L.7,C.19]
        [ClassDeclaration] TestClass @ ./Code2/inc/TestClass.h[L.9,C.19]
          [LastDeclaration]  @ ./Code2/inc/TestClass.h[L.11,C.13]
          [Constructor] TestClass @ ./Code2/inc/TestClass.h[L.12,C.17]
          [Destructor] ~TestClass @ ./Code2/inc/TestClass.h[L.13,C.17]
          [CXXMethod] PublicMethod @ ./Code2/inc/TestClass.h[L.15,C.22]
            [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.15,C.39]
            [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.15,C.46]
          [CXXMethod] GetA @ ./Code2/inc/TestClass.h[L.17,C.21]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.17,C.28]
              [ReturnStatement]  @ ./Code2/inc/TestClass.h[L.17,C.30]
                [UnexposedExpression] m_a @ ./Code2/inc/TestClass.h[L.17,C.43]
                  [MemberReferenceExpression] m_a @ ./Code2/inc/TestClass.h[L.17,C.43]
                    [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.17,C.37]
          [CXXMethod] GetB @ ./Code2/inc/TestClass.h[L.18,C.21]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.18,C.28]
              [ReturnStatement]  @ ./Code2/inc/TestClass.h[L.18,C.30]
                [UnexposedExpression] m_b @ ./Code2/inc/TestClass.h[L.18,C.43]
                  [MemberReferenceExpression] m_b @ ./Code2/inc/TestClass.h[L.18,C.43]
                    [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.18,C.37]
          [CXXMethod] GetResult @ ./Code2/inc/TestClass.h[L.19,C.21]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.19,C.33]
              [ReturnStatement]  @ ./Code2/inc/TestClass.h[L.19,C.35]
                [UnexposedExpression] m_result @ ./Code2/inc/TestClass.h[L.19,C.48]
                  [MemberReferenceExpression] m_result @ ./Code2/inc/TestClass.h[L.19,C.48]
                    [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.19,C.42]
          [CXXMethod] SetA @ ./Code2/inc/TestClass.h[L.21,C.22]
            [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.21,C.31]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.21,C.34]
              [BinaryOperator]  @ ./Code2/inc/TestClass.h[L.21,C.36]
                [MemberReferenceExpression] m_a @ ./Code2/inc/TestClass.h[L.21,C.42]
                  [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.21,C.36]
                [UnexposedExpression] a @ ./Code2/inc/TestClass.h[L.21,C.48]
                  [DeclarationReferenceExpression] a @ ./Code2/inc/TestClass.h[L.21,C.48]
          [CXXMethod] SetB @ ./Code2/inc/TestClass.h[L.22,C.22]
            [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.22,C.31]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.22,C.34]
              [BinaryOperator]  @ ./Code2/inc/TestClass.h[L.22,C.36]
                [MemberReferenceExpression] m_b @ ./Code2/inc/TestClass.h[L.22,C.42]
                  [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.22,C.36]
                [UnexposedExpression] b @ ./Code2/inc/TestClass.h[L.22,C.48]
                  [DeclarationReferenceExpression] b @ ./Code2/inc/TestClass.h[L.22,C.48]
          [CXXMethod] SetResult @ ./Code2/inc/TestClass.h[L.23,C.22]
            [ParmDeclaration] result @ ./Code2/inc/TestClass.h[L.23,C.36]
            [CompoundStatement]  @ ./Code2/inc/TestClass.h[L.23,C.44]
              [BinaryOperator]  @ ./Code2/inc/TestClass.h[L.23,C.46]
                [MemberReferenceExpression] m_result @ ./Code2/inc/TestClass.h[L.23,C.52]
                  [CXXThisExpression]  @ ./Code2/inc/TestClass.h[L.23,C.46]
                [UnexposedExpression] result @ ./Code2/inc/TestClass.h[L.23,C.63]
                  [DeclarationReferenceExpression] result @ ./Code2/inc/TestClass.h[L.23,C.63]
          [LastDeclaration]  @ ./Code2/inc/TestClass.h[L.25,C.13]
          [CXXMethod] PrivateMethod @ ./Code2/inc/TestClass.h[L.26,C.21]
            [ParmDeclaration] a @ ./Code2/inc/TestClass.h[L.26,C.39]
            [ParmDeclaration] b @ ./Code2/inc/TestClass.h[L.26,C.46]
          [LastDeclaration]  @ ./Code2/inc/TestClass.h[L.28,C.13]
          [FieldDeclaration] m_a @ ./Code2/inc/TestClass.h[L.29,C.21]
          [FieldDeclaration] m_b @ ./Code2/inc/TestClass.h[L.30,C.21]
          [FieldDeclaration] m_result @ ./Code2/inc/TestClass.h[L.31,C.21]
  [Namespace] root @ ./Code2/src/TestClass.cpp[L.3,C.11]
    [Namespace] sub1 @ ./Code2/src/TestClass.cpp[L.5,C.15]
      [Namespace] sub2 @ ./Code2/src/TestClass.cpp[L.7,C.19]
        [Constructor] TestClass @ ./Code2/src/TestClass.cpp[L.9,C.24]
          [TypeReference] class root::sub1::sub2::TestClass @ ./Code2/src/TestClass.cpp[L.9,C.13]
          [MemberReference] m_a @ ./Code2/src/TestClass.cpp[L.10,C.19]
          [IntegerLiteral]  @ ./Code2/src/TestClass.cpp[L.10,C.23]
          [MemberReference] m_b @ ./Code2/src/TestClass.cpp[L.11,C.19]
          [IntegerLiteral]  @ ./Code2/src/TestClass.cpp[L.11,C.23]
          [MemberReference] m_result @ ./Code2/src/TestClass.cpp[L.12,C.19]
          [IntegerLiteral]  @ ./Code2/src/TestClass.cpp[L.12,C.28]
          [CompoundStatement]  @ ./Code2/src/TestClass.cpp[L.13,C.13]
        [Destructor] ~TestClass @ ./Code2/src/TestClass.cpp[L.16,C.24]
          [TypeReference] class root::sub1::sub2::TestClass @ ./Code2/src/TestClass.cpp[L.16,C.13]
          [CompoundStatement]  @ ./Code2/src/TestClass.cpp[L.17,C.13]
        [CXXMethod] PublicMethod @ ./Code2/src/TestClass.cpp[L.21,C.29]
          [TypeReference] class root::sub1::sub2::TestClass @ ./Code2/src/TestClass.cpp[L.21,C.18]
          [ParmDeclaration] a @ ./Code2/src/TestClass.cpp[L.21,C.46]
          [ParmDeclaration] b @ ./Code2/src/TestClass.cpp[L.21,C.53]
          [CompoundStatement]  @ ./Code2/src/TestClass.cpp[L.22,C.13]
            [DeclarationStatement]  @ ./Code2/src/TestClass.cpp[L.23,C.17]
              [VarDeclaration] result @ ./Code2/src/TestClass.cpp[L.23,C.21]
                [CallExpression] PrivateMethod @ ./Code2/src/TestClass.cpp[L.23,C.30]
                  [MemberReferenceExpression] PrivateMethod @ ./Code2/src/TestClass.cpp[L.23,C.36]
                    [CXXThisExpression]  @ ./Code2/src/TestClass.cpp[L.23,C.30]
                  [UnexposedExpression] a @ ./Code2/src/TestClass.cpp[L.23,C.50]
                    [DeclarationReferenceExpression] a @ ./Code2/src/TestClass.cpp[L.23,C.50]
                  [UnexposedExpression] b @ ./Code2/src/TestClass.cpp[L.23,C.53]
                    [DeclarationReferenceExpression] b @ ./Code2/src/TestClass.cpp[L.23,C.53]
            [CallExpression] SetResult @ ./Code2/src/TestClass.cpp[L.25,C.17]
              [MemberReferenceExpression] SetResult @ ./Code2/src/TestClass.cpp[L.25,C.23]
                [CXXThisExpression]  @ ./Code2/src/TestClass.cpp[L.25,C.17]
              [UnexposedExpression] result @ ./Code2/src/TestClass.cpp[L.25,C.33]
                [DeclarationReferenceExpression] result @ ./Code2/src/TestClass.cpp[L.25,C.33]
        [CXXMethod] PrivateMethod @ ./Code2/src/TestClass.cpp[L.28,C.28]
          [TypeReference] class root::sub1::sub2::TestClass @ ./Code2/src/TestClass.cpp[L.28,C.17]
          [ParmDeclaration] a @ ./Code2/src/TestClass.cpp[L.28,C.46]
          [ParmDeclaration] b @ ./Code2/src/TestClass.cpp[L.28,C.53]
          [CompoundStatement]  @ ./Code2/src/TestClass.cpp[L.29,C.13]
            [CallExpression] SetA @ ./Code2/src/TestClass.cpp[L.30,C.17]
              [MemberReferenceExpression] SetA @ ./Code2/src/TestClass.cpp[L.30,C.23]
                [CXXThisExpression]  @ ./Code2/src/TestClass.cpp[L.30,C.17]
              [UnexposedExpression] a @ ./Code2/src/TestClass.cpp[L.30,C.28]
                [DeclarationReferenceExpression] a @ ./Code2/src/TestClass.cpp[L.30,C.28]
            [CallExpression] SetB @ ./Code2/src/TestClass.cpp[L.31,C.17]
              [MemberReferenceExpression] SetB @ ./Code2/src/TestClass.cpp[L.31,C.23]
                [CXXThisExpression]  @ ./Code2/src/TestClass.cpp[L.31,C.17]
              [UnexposedExpression] b @ ./Code2/src/TestClass.cpp[L.31,C.28]
                [DeclarationReferenceExpression] b @ ./Code2/src/TestClass.cpp[L.31,C.28]
            [ReturnStatement]  @ ./Code2/src/TestClass.cpp[L.33,C.17]
              [BinaryOperator]  @ ./Code2/src/TestClass.cpp[L.33,C.24]
                [UnexposedExpression] m_a @ ./Code2/src/TestClass.cpp[L.33,C.30]
                  [MemberReferenceExpression] m_a @ ./Code2/src/TestClass.cpp[L.33,C.30]
                    [CXXThisExpression]  @ ./Code2/src/TestClass.cpp[L.33,C.24]
                [UnexposedExpression] m_b @ ./Code2/src/TestClass.cpp[L.33,C.42]
                  [MemberReferenceExpression] m_b @ ./Code2/src/TestClass.cpp[L.33,C.42]
                    [CXXThisExpression]  @ ./Code2/src/TestClass.cpp[L.33,C.36]

--------------------------------------
Invokation Tree:
int root::sub1::sub2::TestClass::GetA() @ C:\Works\Test\Code2\inc\TestClass.h[L.17,C.21]

int root::sub1::sub2::TestClass::GetB() @ C:\Works\Test\Code2\inc\TestClass.h[L.18,C.21]

int root::sub1::sub2::TestClass::GetResult() @ C:\Works\Test\Code2\inc\TestClass.h[L.19,C.21]

void root::sub1::sub2::TestClass::SetA(int) @ C:\Works\Test\Code2\inc\TestClass.h[L.21,C.22]

void root::sub1::sub2::TestClass::SetB(int) @ C:\Works\Test\Code2\inc\TestClass.h[L.22,C.22]

void root::sub1::sub2::TestClass::SetResult(int) @ C:\Works\Test\Code2\inc\TestClass.h[L.23,C.22]

main() @ C:\Works\Test\Code2\src\main.cpp[L.7,C.5]
  root::sub1::sub2::TestClass::TestClass() @ C:\Works\Test\Code2\src\main.cpp[L.9,C.33]
  void root::sub1::sub2::TestClass::SetA(int) @ C:\Works\Test\Code2\src\main.cpp[L.14,C.5]
  void root::sub1::sub2::TestClass::SetB(int) @ C:\Works\Test\Code2\src\main.cpp[L.16,C.5]
  std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl))) @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5]
     <Behavior Definition Not Found...>
  std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int) @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5]
     <Behavior Definition Not Found...>
  std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *) @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5]
     <Behavior Definition Not Found...>
  int root::sub1::sub2::TestClass::GetA() @ C:\Works\Test\Code2\src\main.cpp[L.18,C.28]
  std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl))) @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5]
     <Behavior Definition Not Found...>
  std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int) @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5]
     <Behavior Definition Not Found...>
  std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *) @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5]
     <Behavior Definition Not Found...>
  int root::sub1::sub2::TestClass::GetB() @ C:\Works\Test\Code2\src\main.cpp[L.20,C.28]
  void root::sub1::sub2::TestClass::PublicMethod(int, int) @ C:\Works\Test\Code2\src\main.cpp[L.22,C.5]
    int root::sub1::sub2::TestClass::PrivateMethod(int, int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.23,C.30]
      void root::sub1::sub2::TestClass::SetA(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.30,C.17]
      void root::sub1::sub2::TestClass::SetB(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.31,C.17]
    void root::sub1::sub2::TestClass::SetResult(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.25,C.17]
  int root::sub1::sub2::TestClass::GetResult() @ C:\Works\Test\Code2\src\main.cpp[L.24,C.14]
  std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl))) @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5]
     <Behavior Definition Not Found...>
  std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int) @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5]
     <Behavior Definition Not Found...>
  std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *) @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5]
     <Behavior Definition Not Found...>
  RecursiveCallTest(int) @ C:\Works\Test\Code2\src\main.cpp[L.28,C.5]
    std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl))) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9]
       <Behavior Definition Not Found...>
    std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9]
       <Behavior Definition Not Found...>
    std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9]
       <Behavior Definition Not Found...>
    RecursiveCallTest(int) @ C:\Works\Test\Code2\src\main.cpp[L.41,C.9]
       <Recursive Call...>

RecursiveCallTest(int) @ C:\Works\Test\Code2\src\main.cpp[L.31,C.6]
  std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl))) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9]
     <Behavior Definition Not Found...>
  std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9]
     <Behavior Definition Not Found...>
  std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *) @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9]
     <Behavior Definition Not Found...>
  RecursiveCallTest(int) @ C:\Works\Test\Code2\src\main.cpp[L.41,C.9]
     <Recursive Call...>

root::sub1::sub2::TestClass::TestClass() @ C:\Works\Test\Code2\src\TestClass.cpp[L.9,C.24]

root::sub1::sub2::TestClass::~TestClass() @ C:\Works\Test\Code2\src\TestClass.cpp[L.16,C.24]

void root::sub1::sub2::TestClass::PublicMethod(int, int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.21,C.29]
  int root::sub1::sub2::TestClass::PrivateMethod(int, int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.23,C.30]
    void root::sub1::sub2::TestClass::SetA(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.30,C.17]
    void root::sub1::sub2::TestClass::SetB(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.31,C.17]
  void root::sub1::sub2::TestClass::SetResult(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.25,C.17]

int root::sub1::sub2::TestClass::PrivateMethod(int, int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.28,C.28]
  void root::sub1::sub2::TestClass::SetA(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.30,C.17]
  void root::sub1::sub2::TestClass::SetB(int) @ C:\Works\Test\Code2\src\TestClass.cpp[L.31,C.17]


--------------------------------------
Cross References:
root::sub1::sub2::TestClass::TestClass()
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.9,C.33]
  [Declaration] @ C:\Works\Test\Code2\inc\TestClass.h[L.12,C.17]
  [Definition] @ C:\Works\Test\Code2\src\TestClass.cpp[L.9,C.24]

root::sub1::sub2::TestClass::~TestClass()
  [Declaration] @ C:\Works\Test\Code2\inc\TestClass.h[L.13,C.17]
  [Definition] @ C:\Works\Test\Code2\src\TestClass.cpp[L.16,C.24]

void root::sub1::sub2::TestClass::PublicMethod(int, int)
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.22,C.5]
  [Declaration] @ C:\Works\Test\Code2\inc\TestClass.h[L.15,C.22]
  [Definition] @ C:\Works\Test\Code2\src\TestClass.cpp[L.21,C.29]

int root::sub1::sub2::TestClass::GetA()
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.18,C.28]
  [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.17,C.21]

int root::sub1::sub2::TestClass::GetB()
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.20,C.28]
  [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.18,C.21]

int root::sub1::sub2::TestClass::GetResult()
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.24,C.14]
  [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.19,C.21]

void root::sub1::sub2::TestClass::SetA(int)
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.14,C.5]
  [Call] @ C:\Works\Test\Code2\src\TestClass.cpp[L.30,C.17]
  [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.21,C.22]

void root::sub1::sub2::TestClass::SetB(int)
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.16,C.5]
  [Call] @ C:\Works\Test\Code2\src\TestClass.cpp[L.31,C.17]
  [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.22,C.22]

void root::sub1::sub2::TestClass::SetResult(int)
  [Call] @ C:\Works\Test\Code2\src\TestClass.cpp[L.25,C.17]
  [Definition] @ C:\Works\Test\Code2\inc\TestClass.h[L.23,C.22]

int root::sub1::sub2::TestClass::PrivateMethod(int, int)
  [Call] @ C:\Works\Test\Code2\src\TestClass.cpp[L.23,C.30]
  [Declaration] @ C:\Works\Test\Code2\inc\TestClass.h[L.26,C.21]
  [Definition] @ C:\Works\Test\Code2\src\TestClass.cpp[L.28,C.28]

RecursiveCallTest(int)
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.28,C.5]
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.41,C.9]
  [Declaration] @ C:\Works\Test\Code2\src\main.cpp[L.5,C.6]
  [Definition] @ C:\Works\Test\Code2\src\main.cpp[L.31,C.6]

main()
  [Definition] @ C:\Works\Test\Code2\src\main.cpp[L.7,C.5]

std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(std::basic_ostream<char, std::char_traits<char> > &(*)(std::basic_ostream<char, std::char_traits<char> > &) __attribute__((cdecl)))
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5]
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5]
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5]
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9]

std::basic_ostream<char, std::char_traits<char> > & std::basic_ostream::operator<<(int)
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5]
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5]
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5]
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9]

std::operator<<<>(basic_ostream<char, std::char_traits<char> > &, const char *)
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.18,C.5]
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.20,C.5]
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.26,C.5]
  [Call] @ C:\Works\Test\Code2\src\main.cpp[L.39,C.9]

あとがき

ClangNetを使って以下の情報を出力する機能を実現するサンプルを紹介しました。

  1. 関数コールツリー
  2. 関数クロスリファレンス(宣言/定義/コール)情報

作成したサンプルの全ソースはGitHubにおいてあります。
GitHub : Link

今回作成したサンプルには、テンプレート関数など対応していないケースがあるので、次回はそれらに対応できるように拡張していきたいと思います。

ClangNet(libclangの.NET(C#) Binding) Ver.1.1.0リリース

リリースノート

ClangNetのVer1.1.0をリリースしました。

変更点: 1. LLVM/Clang Ver.9.0.0に対応 2. いくつかの不具合を修正


ダウンロード先&インストール方法

libclang.dllやClangNetは以下からダウンロードしてください。


LLVM/Clang Ver.9.0.0 : Link

GitHub : Link

NuGet : Link


インストール方法や使い方は以下の記事を参照してください。

an-embedded-engineer.hateblo.jp

ClangNet(libclangの.NET(C#) Binding)によるC/C++ソースコードの解析 - その1

まえがき

LLVM/Clangに同梱されているlibclangの.NET(C#) Bindingライブラリ(ClangNet)を作成しました。
libclangはC/C++/Objective-Cソースコードの解析や自動補完機能を提供する高レベルのAPIで、オリジナルはC言語ベースのAPIが提供されています。


C言語のままだとかなり使い勝手が悪いため、.NETのP/Invoke機能(マネージドコードから動的ライブラリ内のネイティブコードを呼び出す仕組み)を用いてlibclangのWrapperを作成し、C#扱いやすいようにクラスベースのライブラリ化しました。


プロジェクト一式はGitHubにアップロードしています。


なお、以下の環境でビルドおよび簡易的な動作確認を行っています。

※ すべての機能に対するテストは出来ていませんので、不具合等ありましたらご連絡いただければと思います。


準備

ClangNetを使用するためには、LLVM/Clang内に同梱されているlibclang(.dllや.dylib)を取得する必要があります。
以下はWindowsの場合の例を記載します。

  1. 公式からWindows 64bit用のインストーラをダウンロード (現在はVer.8.0.1に対応しています)
  2. ダウンロードしたインストーラからLLVM/Clangをインストール
  3. LLVM/Clangインストール先からlibclang.dllを取得 (デフォルでは、"C:\Program Files\LLVM\bin\llvmclang.dll)
  4. ClangNetを使ったプログラムの実行ファイルと同じ場所にlibclang.dllを配置 (GitHubのサンプルでは、「ClangNet.CUI」プロジェクトをビルドして実行ファイルが生成されたディレクトリ内に配置します)
  5. ClangNetを使ったプログラムを実行

MacLinuxの場合も同様にして、libclangのライブラリファイルを実行ファイルディレクトリ内に配置することで対応できると思います (拡張子は、Macの場合は.dylib、Linuxの場合は.soとなります)。


サンプル

今回は、基本的な使い方として、以下の2種類のサンプルを用意しました。

※ 下記に記載しているサンプルコードは、必要な部分のみに絞って記載していますので、GitHubのコードとは異なります。 動作可能なサンプルはGitHubから取得してください。


解析対象ソース

今回は解析対象ソースとして以下のような簡単なヘッダとソースファイルを用意しました。


[test.h]

int main(void);
int Add(int a, int b);

class TestClass
{
public:
    TestClass()
    {
        this->m_a = 0;
        this->m_b = 0;
    }

    TestClass(int a, int b);

    ~TestClass(){}

    int Add();

private:
    int m_a;
    int m_b;
};


[test.cpp]

#include <iostream>
#include "test.h"

using namespace std;

int Add(int a, int b)
{
    return a + b;
}

TestClass::TestClass(int a, int b)
{
    this->m_a = a;
    this->m_b = b;
}

int TestClass::Add()
{
    return this->m_a + this->m_b;
}

int main(void)
{
    cout << "Hello, World" << endl;

    int a = 1;
    int b = 2;

    int c = Add(a, b);

    return 0;
}


AST(Abstract Syntax Tree)の解析
using ClangNet;

public void Parse(string src_path,                // ソースファイルパス
                  string[] command_line_args,     // clang実行時に指定するオプション
                  TranslationUnitFlags options,   // Translation Unit解析オプション
                  bool display_diag)              // ダイアグメッセージ表示フラグ
{
    // インデックスを作成
    using(var index = Clang.CreateIndex(false, display_diag))
    {
        // ソースファイルをパースし、Translation Unit Objectを作成
        using(var tu = index.ParseTranslationUnit(src_path, command_line_args, new ClangUnsavedFile[0], options))
        {
            // Translation Unitのカーソルを取得
            var cursor = tu.Cursor;

            // Cursor VisitorとASTの深さ(Depth)を指定して、カーソルの子要素を探索
            cursor.VisitChildren(this.Visitor, 0);
        }
    }
}

private ChildVisitResult Visitor(ClangCursor cursor,  // 現在のカーソル
                                 ClangCursor parent,  // 親カーソル
                                 int depth)           // 現在の深さ
{
    // カーソル位置を取得
    var loc = cursor.Location;

    // カーソルが解析対象ソースファイルの場合
    if (loc.IsFromMainFile == true)
    {
        // 解析処理を呼び出し
        this.VisitChild(cursor, depth);
    }
    else
    {
        /* システムインクルードヘッダ内 */
        if (loc.IsInSystemHeader == true)
        {
            /* 無視 */
        }
        else
        {
            // 解析処理を呼び出し
            this.VisitChild(cursor, depth);
        }
    }

    return ChildVisitResult.Continue;
}

private void VisitChild(ClangCursor cursor, int depth)
{
    // インデント用空白文字
    var indent = new string(' ', depth * 2);

    // カーソルの種別
    var kind = cursor.Kind;

    // カーソルが指す文字列
    var name = cursor.Spelling;

    // カーソル位置
    var loc = cursor.Location;

    // カーソルの実ファイル位置
    var floc = loc.FileLocation;

    // ファイル名
    var file = floc.File.FileName;

    // ファイル行番号
    var line = floc.Line;

    // ファイル列番号
    var col = floc.Column;

    // カーソル情報を出力
    Console.WriteLine($"{indent}[{kind}] {name} @ {file}[L.{line},C.{col}]");

    // Cursor VisitorとASTの深さ(現在のDepth + 1)を指定して、カーソルの子要素を探索
    cursor.VisitChildren(this.Visitor, depth + 1);
}


実行結果は以下のようになります。

[FunctionDeclaration] main @ ./Code/inc/test.h[L.1,C.5]
[FunctionDeclaration] Add @ ./Code/inc/test.h[L.2,C.5]
  [ParmDeclaration] a @ ./Code/inc/test.h[L.2,C.13]
  [ParmDeclaration] b @ ./Code/inc/test.h[L.2,C.20]
[ClassDeclaration] TestClass @ ./Code/inc/test.h[L.4,C.7]
  [CXXAccessSpecifier]  @ ./Code/inc/test.h[L.6,C.1]
  [Constructor] TestClass @ ./Code/inc/test.h[L.7,C.5]
    [CompoundStatement]  @ ./Code/inc/test.h[L.8,C.5]
      [BinaryOperator]  @ ./Code/inc/test.h[L.9,C.9]
        [MemberReferenceExpression] m_a @ ./Code/inc/test.h[L.9,C.15]
          [CXXThisExpression]  @ ./Code/inc/test.h[L.9,C.9]
        [IntegerLiteral]  @ ./Code/inc/test.h[L.9,C.21]
      [BinaryOperator]  @ ./Code/inc/test.h[L.10,C.9]
        [MemberReferenceExpression] m_b @ ./Code/inc/test.h[L.10,C.15]
          [CXXThisExpression]  @ ./Code/inc/test.h[L.10,C.9]
        [IntegerLiteral]  @ ./Code/inc/test.h[L.10,C.21]
  [Constructor] TestClass @ ./Code/inc/test.h[L.13,C.5]
    [ParmDeclaration] a @ ./Code/inc/test.h[L.13,C.19]
    [ParmDeclaration] b @ ./Code/inc/test.h[L.13,C.26]
  [Destructor] ~TestClass @ ./Code/inc/test.h[L.15,C.5]
    [CompoundStatement]  @ ./Code/inc/test.h[L.15,C.17]
  [CXXMethod] Add @ ./Code/inc/test.h[L.17,C.9]
  [CXXAccessSpecifier]  @ ./Code/inc/test.h[L.19,C.1]
  [FieldDeclaration] m_a @ ./Code/inc/test.h[L.20,C.9]
  [FieldDeclaration] m_b @ ./Code/inc/test.h[L.21,C.9]
[UsingDirective]  @ ./Code/src/test.cpp[L.4,C.17]
  [NamespaceReference] std @ ./Code/src/test.cpp[L.4,C.17]
[FunctionDeclaration] Add @ ./Code/src/test.cpp[L.6,C.5]
  [ParmDeclaration] a @ ./Code/src/test.cpp[L.6,C.13]
  [ParmDeclaration] b @ ./Code/src/test.cpp[L.6,C.20]
  [CompoundStatement]  @ ./Code/src/test.cpp[L.7,C.1]
    [ReturnStatement]  @ ./Code/src/test.cpp[L.8,C.5]
      [BinaryOperator]  @ ./Code/src/test.cpp[L.8,C.12]
        [FirstExpression] a @ ./Code/src/test.cpp[L.8,C.12]
          [DeclarationReferenceExpression] a @ ./Code/src/test.cpp[L.8,C.12]
        [FirstExpression] b @ ./Code/src/test.cpp[L.8,C.16]
          [DeclarationReferenceExpression] b @ ./Code/src/test.cpp[L.8,C.16]
[Constructor] TestClass @ ./Code/src/test.cpp[L.11,C.12]
  [TypeReference] class TestClass @ ./Code/src/test.cpp[L.11,C.1]
  [ParmDeclaration] a @ ./Code/src/test.cpp[L.11,C.26]
  [ParmDeclaration] b @ ./Code/src/test.cpp[L.11,C.33]
  [CompoundStatement]  @ ./Code/src/test.cpp[L.12,C.1]
    [BinaryOperator]  @ ./Code/src/test.cpp[L.13,C.5]
      [MemberReferenceExpression] m_a @ ./Code/src/test.cpp[L.13,C.11]
        [CXXThisExpression]  @ ./Code/src/test.cpp[L.13,C.5]
      [FirstExpression] a @ ./Code/src/test.cpp[L.13,C.17]
        [DeclarationReferenceExpression] a @ ./Code/src/test.cpp[L.13,C.17]
    [BinaryOperator]  @ ./Code/src/test.cpp[L.14,C.5]
      [MemberReferenceExpression] m_b @ ./Code/src/test.cpp[L.14,C.11]
        [CXXThisExpression]  @ ./Code/src/test.cpp[L.14,C.5]
      [FirstExpression] b @ ./Code/src/test.cpp[L.14,C.17]
        [DeclarationReferenceExpression] b @ ./Code/src/test.cpp[L.14,C.17]
[CXXMethod] Add @ ./Code/src/test.cpp[L.17,C.16]
  [TypeReference] class TestClass @ ./Code/src/test.cpp[L.17,C.5]
  [CompoundStatement]  @ ./Code/src/test.cpp[L.18,C.1]
    [ReturnStatement]  @ ./Code/src/test.cpp[L.19,C.5]
      [BinaryOperator]  @ ./Code/src/test.cpp[L.19,C.12]
        [FirstExpression] m_a @ ./Code/src/test.cpp[L.19,C.18]
          [MemberReferenceExpression] m_a @ ./Code/src/test.cpp[L.19,C.18]
            [CXXThisExpression]  @ ./Code/src/test.cpp[L.19,C.12]
        [FirstExpression] m_b @ ./Code/src/test.cpp[L.19,C.30]
          [MemberReferenceExpression] m_b @ ./Code/src/test.cpp[L.19,C.30]
            [CXXThisExpression]  @ ./Code/src/test.cpp[L.19,C.24]
[FunctionDeclaration] main @ ./Code/src/test.cpp[L.22,C.5]
  [CompoundStatement]  @ ./Code/src/test.cpp[L.23,C.1]
    [CallExpression] operator<< @ ./Code/src/test.cpp[L.24,C.5]
      [CallExpression] operator<< @ ./Code/src/test.cpp[L.24,C.5]
        [DeclarationReferenceExpression] cout @ ./Code/src/test.cpp[L.24,C.5]
        [FirstExpression] operator<< @ ./Code/src/test.cpp[L.24,C.10]
          [DeclarationReferenceExpression] operator<< @ ./Code/src/test.cpp[L.24,C.10]
        [FirstExpression]  @ ./Code/src/test.cpp[L.24,C.13]
          [StringLiteral] "Hello, World" @ ./Code/src/test.cpp[L.24,C.13]
      [FirstExpression] operator<< @ ./Code/src/test.cpp[L.24,C.28]
        [DeclarationReferenceExpression] operator<< @ ./Code/src/test.cpp[L.24,C.28]
      [FirstExpression] endl @ ./Code/src/test.cpp[L.24,C.31]
        [DeclarationReferenceExpression] endl @ ./Code/src/test.cpp[L.24,C.31]
    [DeclarationStatement]  @ ./Code/src/test.cpp[L.26,C.5]
      [VarDeclaration] a @ ./Code/src/test.cpp[L.26,C.9]
        [IntegerLiteral]  @ ./Code/src/test.cpp[L.26,C.13]
    [DeclarationStatement]  @ ./Code/src/test.cpp[L.27,C.5]
      [VarDeclaration] b @ ./Code/src/test.cpp[L.27,C.9]
        [IntegerLiteral]  @ ./Code/src/test.cpp[L.27,C.13]
    [DeclarationStatement]  @ ./Code/src/test.cpp[L.29,C.5]
      [VarDeclaration] c @ ./Code/src/test.cpp[L.29,C.9]
        [CallExpression] Add @ ./Code/src/test.cpp[L.29,C.13]
          [FirstExpression] Add @ ./Code/src/test.cpp[L.29,C.13]
            [DeclarationReferenceExpression] Add @ ./Code/src/test.cpp[L.29,C.13]
          [FirstExpression] a @ ./Code/src/test.cpp[L.29,C.17]
            [DeclarationReferenceExpression] a @ ./Code/src/test.cpp[L.29,C.17]
          [FirstExpression] b @ ./Code/src/test.cpp[L.29,C.20]
            [DeclarationReferenceExpression] b @ ./Code/src/test.cpp[L.29,C.20]
    [ReturnStatement]  @ ./Code/src/test.cpp[L.31,C.5]
      [IntegerLiteral]  @ ./Code/src/test.cpp[L.31,C.12]


インクルードファイルの依存関係解析
using ClangNet;

public void Parse(string src_path,                // ソースファイルパス
                  string[] command_line_args,     // clang実行時に指定するオプション
                  TranslationUnitFlags options,   // Translation Unit解析オプション
                  bool display_diag)              // ダイアグメッセージ表示フラグ
{
    // インデックスを作成
    using(var index = Clang.CreateIndex(false, display_diag))
    {
        // ソースファイルをパースし、Translation Unit Objectを作成
        using(var tu = index.ParseTranslationUnit(src_path, command_line_args, new ClangUnsavedFile[0], options))
        {
            // Translation Unitのカーソルを取得
            var cursor = tu.Cursor;

            // Visitorを指定してインクルードファイルを再帰的に走査
            tu.GetInclusions(this.Visitor, IntPtr.Zero);
        }
    }
}

private void Visitor(ClangFile file,                        // 現在のファイル
                     ClangSourceLocation[] location_stack,  // インクルード位置のスタック
                     IntPtr client_data)                    // クライアントデータへのポインタ
{
    // インクルードの深さ
    var depth = location_stack.Length;

    // インデント用空白文字
    var indent = new string(' ', depth * 2);

    // インクルードファイルのフルパス
    var filename = Path.GetFullPath(file.FileName());

    // インクルードファイルパスの出力
    Console.WriteLine($"{indent}{filename}");
}


実行結果は以下のようになります。

C:/Users/shin/source/repos/ClangNet/ClangNet.CUI/bin/Debug/netcoreapp2.1/Code/src/test.cpp
  C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/iostream
    C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/istream
      C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/ostream
        C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/ios
          C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xlocnum
            C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/climits
              C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/yvals_core.h
                C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/crtdefs.h
                  C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/vcruntime.h
                    C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/sal.h
                      C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/concurrencysal.h
                    C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/vadefs.h
                  C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt.h
                C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xkeycheck.h
              C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/limits.h
            C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/cmath
              C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/yvals.h
                C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/crtdbg.h
                  C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/vcruntime_new_debug.h
                    C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/vcruntime_new.h
                C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/use_ansi.h
              C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/cstdlib
                C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/math.h
                  C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_math.h
                C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/stdlib.h
                  C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_malloc.h
                  C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_search.h
                    C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/stddef.h
                  C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_wstdlib.h
              C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xtgmath.h
                C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xtr1common
            C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/cstdio
              C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/stdio.h
                C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_wstdio.h
                  C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_stdio_config.h
            C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/streambuf
              C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xiosbase
                C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/share.h
                  C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_share.h
                C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/system_error
                  C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/cerrno
                    C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/errno.h
                  C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/stdexcept
                    C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/exception
                      C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/type_traits
                        C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xstddef
                          C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/cstddef
                          C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/initializer_list
                      C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/malloc.h
                      C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/vcruntime_exception.h
                        C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/eh.h
                          C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_terminate.h
                    C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xstring
                      C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/iosfwd
                        C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/cstring
                          C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/string.h
                            C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_memory.h
                              C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_memcpy_s.h
                                C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/vcruntime_string.h
                            C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_wstring.h
                        C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/cwchar
                          C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/wchar.h
                            C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_wconio.h
                            C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_wctype.h
                            C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_wdirect.h
                            C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_wio.h
                            C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_wprocess.h
                            C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/corecrt_wtime.h
                            C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/sys/stat.h
                              C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/sys/types.h
                      C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xmemory
                        C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/cstdint
                          C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/stdint.h
                        C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/limits
                          C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/cfloat
                            C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/float.h
                        C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/new
                        C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xatomic.h
                          C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/intrin0.h
                        C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xutility
                          C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/utility
                  C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xcall_once.h
                  C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xerrc.h
                C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xlocale
                  C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/memory
                    C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/typeinfo
                      C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/vcruntime_typeinfo.h
                  C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xfacet
                  C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xlocinfo
                    C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include/xlocinfo.h
                      C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/ctype.h
                      C:/Program Files (x86)/Windows Kits/10/Include/10.0.18362.0/ucrt/locale.h
  C:/Users/<user>/source/repos/ClangNet/ClangNet.CUI/bin/Debug/netcoreapp2.1/Code/inc/test.h


Windows用libclangでは、システムインクルードディレクトリとして、デフォルトでVisual StudioWindows Kitsが設定されるようです。
同じコードをMacで動かすと、システムインクルードの方は何も出力されないため、command_line_argsに-I オプションでインクルードパスを指定する必要があります。


あとがき

今後は、もう少し詳しい使い方を紹介していきます。

Python学習メモ - その8

まえがき

最近、Python を勉強し始めたので、その学習メモです。

今回はディクショナリ(Dictionary)についてです。


ディクショナリ(Dictionary)

ディクショナリはユニーク(一意)なキーとそれに対応する値を格納するコレクションです。
例えば、IDとそれに対応する顧客名など、重複する可能性のある値(顧客名)を別の重複しないキー(ID)で管理したい場合などに使用します。


ディクショナリ(単一型)

ディクショナリはキーと値のセット(要素)をコロン(:)で表し、複数の要素をカンマ( , )で区切り、前後に中かっこ「{ }」をつけることでディクショナリとして認識されます。

dict_values = {"A": 1, "B": 2, "C": 3, "D": 4}
print(dict_values)

実行結果

{'A': 1, 'B': 2, 'C': 3, 'D': 4}


ディクショナリ(複数型)

一意の値であれば、異なる型のデータをキーに使用することができます。
また、キーに対する値にも異なる型を格納することができます。

dict_values = {"A": 1, "B": 3.14, 3.5: (1 + 2j), "4": 4}
print(dict_values)

実行結果

{'A': 1, 'B': 3.14, 3.5: (1+2j), '4': 4}


重複定義

ディクショナリの定義時に、重複したキーで初期化した場合、最後にセットされたキーの値が有効となります(最後の値で上書きされる)。

dict_values = {"A": 1, "B": 2, "C": 3, "D": 4, "A": 5}
print(dict_values)

実行結果

{'A': 5, 'B': 2, 'C': 3, 'D': 4}


値の取得(エラーあり)

ディクショナリに対して、キーを指定することで値を取得することができます。
この方法では、存在しないキーを指定すると実行エラーとなります。

dict_values = {"A": 1, "B": 2, "C": 3}
print(dict_values)
print(dict_values["A"])
print(dict_values["B"])
print(dict_values["C"])
# print(dict_values["D"])  # 実行エラー

実行結果

{'A': 1, 'B': 2, 'C': 3}
1
2
3


値の取得(エラーなし or デフォルト値)

getメソッドを使用することでも、キーを指定した値の取得が可能です。
この場合、存在しないキーを指定するとNoneが返されます。
また、存在しないキーを指定した場合に返されるデフォルト値を指定することもできます。

dict_values = {"A": 1, "B": 2, "C": 3}
print(dict_values)
print(dict_values.get("A"))
print(dict_values.get("B"))
print(dict_values.get("C"))
print(dict_values.get("D"))
print(dict_values.get("D", "Unknown Key"))

実行結果

{'A': 1, 'B': 2, 'C': 3}
1
2
3
None
Unknown Key


値の変更

キーを指定して、値を格納することで、値を変更することができます。

dict_values = {"A": 1, "B": 2, "C": 3}
print(dict_values)
dict_values["B"] = 4
print(dict_values)

実行結果

{'A': 1, 'B': 2, 'C': 3}
{'A': 1, 'B': 4, 'C': 3}


空ディクショナリ

空(要素数0)のディクショナリを作成したい場合には、「{}」のみを記述します。

dict_values = {}
print(dict_values)

実行結果

{}


追加

存在しないキーを指定して、値を格納することで要素を追加することができます。

dict_values = {}
dict_values["A"] = 1
dict_values["B"] = 2
dict_values["C"] = 3

print(dict_values)

実行結果

{'A': 1, 'B': 2, 'C': 3}


値の削除

キーを指定してdel演算子を使用することで指定したキーの要素を削除することができます。

dict_values = {"A": 1, "B": 2, "C": 3}

del dict_values["B"]

print(dict_values)

実行結果

{'A': 1, 'C': 3}


値の取り出し(キー指定)

popメソッドを使用することで、指定したキーに対応する値を取り出し(取得 & 削除)することができます。
また、存在しないキーを指定した場合に返されるデフォルト値を指定することもできます。
デフォルト値を指定しないで存在しないキーを指定した場合は、実行エラーとなります。

dict_values = {"A": 1, "B": 2, "C": 3}

pop_value = dict_values.pop("B")
print(pop_value)

pop_value = dict_values.pop("D", "Not Found")
print(pop_value)

# pop_value = dict_values.pop("D")  # 実行エラー

print(dict_values)

実行結果

2
Not Found
{'A': 1, 'C': 3}


キー&値の取り出し

popitemメソッドを使用することで、最後に追加された要素から順番に要素(キー&値)を取り出し(取得&削除)することができます。
空のディクショナリに対してpopitemメソッドを使用すると実行エラーとなります。

dict_values = {"A": 1, "B": 2, "C": 3}
pop_value = dict_values.popitem()
print(pop_value)
pop_value = dict_values.popitem()
print(pop_value)
pop_value = dict_values.popitem()
print(pop_value)
# pop_value = dict_values.popitem() # 実行エラー

実行結果

('C', 3)
('B', 2)
('A', 1)


クリア

ディクショナリをクリアする場合には、clearメソッドを使用します。

dict_values = {"A": 1, "B": 2, "C": 3}
dict_values.clear()
print(dict_values)

実行結果

{}


デフォルト指定セット

setdefaultメソッドを指定すると、以下のような処理を行うことができます。
1. すでに存在するキーを指定した場合は、そのキーに対応する値を返す 2. 存在しないキーとデフォルト値を指定した場合は、そのキーとデフォルト値で要素を追加する 3. 存在しないキーのみを指定した場合は、そのキーとNoneで要素を追加する

dict_values = {"A": 1, "B": 2, "C": 3}
set_value = dict_values.setdefault("A", 4)
print(set_value)
set_value = dict_values.setdefault("D", 4)
print(set_value)
set_value = dict_values.setdefault("E")
print(set_value)
print(dict_values)

実行結果

1
4
None
{'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': None}


更新

updateメソッドを使用することで、ディクショナリを更新することができます。
複数の要素を一気に変更したい場合に便利です。

dict_values = {"A": 1, "B": 2, "C": 3}
dict_values.update([("B", 4), ("D", 4)])
dict_values.update(D=5, E=6)
print(dict_values)

実行結果

{'A': 1, 'B': 4, 'C': 3, 'D': 5, 'E': 6}


全要素数取得

ディクショナリ全体の要素数を取得したい場合はlen関数を使用します。

dict_values = {"A": 1, "B": 2, "C": 3}
print(len(dict_values))

実行結果

3


コピー

ディクショナリの複製を作成したい場合は、copyメソッドを使用します。

src_dict_values = {"A": 1, "B": 2, "C": 3}
dst_dict_values1 = src_dict_values
dst_dict_values2 = src_dict_values.copy()

print(src_dict_values)
print(dst_dict_values1)
print(dst_dict_values2)

print()

src_dict_values["B"] = 4
dst_dict_values2["D"] = 4

print(src_dict_values)
print(dst_dict_values1)
print(dst_dict_values2)

実行結果

{'A': 1, 'B': 2, 'C': 3}
{'A': 1, 'B': 2, 'C': 3}
{'A': 1, 'B': 2, 'C': 3}

{'A': 1, 'B': 4, 'C': 3}
{'A': 1, 'B': 4, 'C': 3}
{'A': 1, 'B': 2, 'C': 3, 'D': 4}


キーの存在確認

ディクショナリ内に特定のキーを持つ要素が存在しているかを確認する場合には、in演算子を使用します。

dict_values = {"A": 1, "B": 2, "C": 3}
print("B" in dict_values)

実行結果

True


逆に、ディクショナリの中に特定のキーを持つ要素が含まれていないことを確認したい場合は、not in 演算子を使用します。

dict_values = {"A": 1, "B": 2, "C": 3}
print("D" not in dict_values)

実行結果

True


キー一覧(View Object)の取得

ディクショナリに格納されている要素のキー一覧を取得したい場合は、keysメソッドを使用します。
keysメソッドから返されるオブジェクトはView Objectとなります。
View Objectは動的なビューを提供し、ディクショナリの要素が変更された場合にその変更内容が自動的に反映されます。

dict_values = {"A": 1, "B": 2, "C": 3}
keys = dict_values.keys()
print(keys)
dict_values["D"] = 4
print(keys)
print(list(keys))

実行結果

dict_keys(['A', 'B', 'C'])
dict_keys(['A', 'B', 'C', 'D'])
['A', 'B', 'C', 'D']


値一覧(View Object)の取得

ディクショナリに格納されている要素の値一覧を取得したい場合は、valuesメソッドを使用します。
valuesメソッドから返されるオブジェクトもkeysメソッドと同様View Objectとなります。

dict_values = {"A": 1, "B": 2, "C": 3}
values = dict_values.values()
print(values)
dict_values["D"] = 4
print(values)
print(list(values))

実行結果

dict_values([1, 2, 3])
dict_values([1, 2, 3, 4])
[1, 2, 3, 4]


要素一覧(View Object)の取得

ディクショナリに格納されている要素一覧を取得したい場合は、itemsメソッドを使用します。
itemsメソッドから返されるオブジェクトもkeysメソッドやvaluesメソッドと同様View Objectとなります。

dict_values = {"A": 1, "B": 2, "C": 3}
items = dict_values.items()
print(items)
dict_values["D"] = 4
print(items)
print(list(items))

実行結果

dict_items([('A', 1), ('B', 2), ('C', 3)])
dict_items([('A', 1), ('B', 2), ('C', 3), ('D', 4)])
[('A', 1), ('B', 2), ('C', 3), ('D', 4)]


参考文献

Python 公式リファレンス

https://docs.python.org/ja/3.7/index.html

Python-izm

https://www.python-izm.com/

ゲームを作りながら楽しく学べるPythonプログラミング

エキスパートPythonプログラミング

エキスパートPythonプログラミング 改訂2版 (アスキードワンゴ)

エキスパートPythonプログラミング 改訂2版 (アスキードワンゴ)

Python学習メモ - その7

まえがき

最近、Python を勉強し始めたので、その学習メモです。

今回はセット(Set)についてです。


セット(Set)

セットはタプルやリストと同様、複数のデータをまとめて扱う場合に使用しますが、ユニーク(一意)な値を格納する順序なしのコレクションです。
例えば、複数のデータから重複したデータを除去したい場合や、数学集合に関する計算を行う場合に使用します。


セット定義 & アクセス

セットは複数のデータをカンマ( , )で区切り、前後に中かっこ「{ }」をつけることでセットとして認識されます。
セットには順序が記憶されないため、タプルやリストのようなインデックスによるアクセス(値取得・変更)はできません。

set_values = {1, 2, 3, 4}
print(set_values)

set_values = {1, 2, 3, 2, 4, 1}
print(set_values)

# print(set_values[0])  # 実行エラー
# set_values[2] = 10    # 実行エラー

実行結果

{1, 2, 3, 4}
{1, 2, 3, 4}


複数型

一意の値であれば、異なる型のデータを格納することもできます。

set_values = {1, 3.14, "set"}
print(set_values)

set_values = {1, 3.14, 1, "set", 3.14}
print(set_values)

実行結果

{1, 3.14, 'set'}
{1, 3.14, 'set'}


空セット

空のセットを定義する場合は、set()関数を使用します。

set_values = set()

print(set_values)

実行結果

set()


値の追加

値の追加はaddメソッドを使用します。

set_values = set()
set_values.add(1)
set_values.add(2)
set_values.add(3)
set_values.add(2)
set_values.add(4)
set_values.add(1)

print(set_values)

実行結果

{1, 2, 3, 4}


値の削除

値の削除はremoveメソッドまたはdiscardメソッドを使用します。
removeメソッドは値が見つからなかった場合に、エラーが発生します。
discardメソッドは値が見つからなかった場合でもエラーが発生しません。

set_values = {1, 2, 3, 4, 5}

set_values.remove(3)
# set_values.remove(6) # Key Error

実行結果

{1, 2, 4, 5}


set_values = {1, 2, 3, 4, 5}

set_values.discard(3)
set_values.discard(6)

print(set_values)

実行結果

{1, 2, 4, 5}


値の取り出し

セットから順番に値を取得する場合は、popメソッドを使用します。 セットがからの場合、エラーが発生します。

set_values = {1, 2, 3, 4, 5}
print(set_values.pop())
print(set_values.pop())
print(set_values.pop())
print(set_values.pop())
print(set_values.pop())
# print(set_values.pop())  # Error

実行結果

1
2
3
4
5


クリア

セットをクリアする場合には、clearメソッドを使用します。

set_values = {1, 2, 3, 4, 5}
print(set_values)

set_values.clear()
print(set_values)

実行結果

{1, 2, 3, 4, 5}
set()


全要素数取得

セット全体の要素数を取得したい場合はlen関数を使用します。

set_values = {1, 2, 3, 4, 5}

print(len(set_values))

実行結果

5


存在確認

セット内に特定の要素が存在しているかを確認する場合には、in演算子を使用します。

set_values = {1, 2, 3, 4, 5}

print(2 in set_values)

実行結果

True


逆に、セットの中に特定の値が含まれていないことを確認したい場合は、not in 演算子を使用します。

set_values = {1, 2, 3, 4, 5}

print(6 in set_values)

実行結果

True


コピー

セットの複製を作成したい場合は、copyメソッドを使用します。

set_values1 = {1, 2, 3, 4, 5}
set_values2 = set_values1
set_values3 = set_values1.copy()

print(set_values1)
print(set_values2)
print(set_values3)

print()

set_values1.add(6)
set_values2.add(7)
print(set_values1)
print(set_values2)
print(set_values3)

実行結果

{1, 2, 3, 4, 5}
{1, 2, 3, 4, 5}
{1, 2, 3, 4, 5}

{1, 2, 3, 4, 5, 6, 7}
{1, 2, 3, 4, 5, 6, 7}
{1, 2, 3, 4, 5}


互いに素

2つのセットが互いに素である(set1とset2が共通の要素を持たない)ことを確認する場合は、isdisjointメソッドを使用します。

set1 = {1, 3, 5, 7}
set2 = {2, 4, 6, 8}

print(set1.isdisjoint(set2))

実行結果

True


部分集合

2つのセットが部分集合の関係である(set1のすべての要素がset2に含まれる(set1とset2が同じでも良い))ことを確認する場合は、issubsetメソッドまたは <= 演算子を使用します。

set1 = {1, 3, 5}

set2 = {1, 2, 3, 4, 5}
print(set1.issubset(set2))
print(set1 <= set2)

print()

set2 = {1, 3, 5}
print(set1.issubset(set2))
print(set1 <= set2)

実行結果

True
True

True
True


真部分集合

2つのセットが真部分集合の関係である(set1のすべての要素がset2に含まれる かつ set1とset2は同じではない)ことを確認する場合は、< 演算子を使用します。 これは、set1.issubset(set2) and set1 != set2と等価です。

set1 = {1, 3, 5}

set2 = {1, 2, 3, 4, 5}
print(set1.issubset(set2) and set1 != set2)
print(set1 < set2)

print()

set2 = {1, 3, 5}
print(set1.issubset(set2) and set1 != set2)
print(set1 < set2)

実行結果

True
True

False
False


上位集合

2つのセットが上位集合の関係である(set2のすべての集合がset1に含まれる(set1とset2が同じでも良い))ことを確認する場合は、issupersetメソッドまたは >= 演算子を使用します。

set1 = {1, 2, 3, 4, 5}

set2 = {1, 3, 5}
print(set1.issuperset(set2))
print(set1 >= set2)

print()

set2 = {1, 2, 3, 4, 5}
print(set1.issuperset(set2))
print(set1 >= set2)

実行結果

True
True

True
True


真上位集合

2つのセットが真上位集合の関係である(set2のすべての要素がset1に含まれる かつ set1とset2は同じではない)ことを確認する場合は、> 演算子を使用します。 これは、set1.issuperset(set2) and set1 != set2と等価です。

set1 = {1, 2, 3, 4, 5}

set2 = {1, 3, 5}
print(set1.issuperset(set2) and set1 != set2)
print(set1 > set2)

print()

set2 = {1, 2, 3, 4, 5}
print(set1.issuperset(set2) and set1 != set2)
print(set1 > set2)

実行結果

True
True

False
False


和集合の作成

set1とset2の要素からなる新しい集合(和集合)を作成する場合、unionメソッドまたは | 演算子を使用します。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}
set3 = set1.union(set2)
set4 = set1 | set2

print(set3)
print(set4)

実行結果

{1, 2, 3, 4, 5}
{1, 2, 3, 4, 5}


積集合の作成

set1とset2の共通要素を持つ新しい集合(積集合)を作成する場合、intersectionメソッドまたは & 演算子を使用します。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}
set3 = set1.intersection(set2)
set4 = set1 & set2

print(set3)
print(set4)

実行結果

{1, 3}
{1, 3}


差集合の作成

set1に含まれset2に含まれない要素をもつ新しい集合(差集合)を作成する場合、differenceメソッドまたは - 演算子を使用します。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}
set3 = set1.difference(set2)
set4 = set1 - set2

print(set3)
print(set4)

実行結果

{2, 4}
{2, 4}


排他集合の作成

set1とset2のいずれか一方にだけ含まれる要素をもつ新しい集合(排他集合)を作成する場合、symmetric_differenceメソッドまたは ^ 演算子を使用します。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}
set3 = set1.symmetric_difference(set2)
set4 = set1 ^ set2

print(set3)
print(set4)

実行結果

{2, 4, 5}
{2, 4, 5}


和更新

set1にset2の要素を追加し、set1を更新(和更新)する場合には、updateメソッドまたは |= 演算子を使用します。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}

set1.update(set2)

print(set1)

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}

set1 |= set2

print(set1)

実行結果

{1, 2, 3, 4, 5}
{1, 2, 3, 4, 5}


積更新

set1とset2に共通する要素を残し、set1を更新(積更新)する場合には、intersection_updateメソッドまたは、 &= 演算子を使用します。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}

set1.intersection_update(set2)

print(set1)

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}

set1 &= set2

print(set1)

実行結果

{1, 3}
{1, 3}


差更新

set1からset2に含まれる要素を取り除き、set1を更新(差更新)する場合には、difference_updateメソッドまたは -= 演算子を使用します。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}

set1.difference_update(set2)

print(set1)

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}

set1 -= set2

print(set1)

実行結果

{2, 4}
{2, 4}


排他更新

set1とset2のどちらかのみに含まれる要素によって、set1を更新(排他更新)する場合には、symmetric_difference_updateメソッドまたは ^= 演算子を使用します。

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}

set1.symmetric_difference_update(set2)

print(set1)

set1 = {1, 2, 3, 4}
set2 = {1, 3, 5}

set1 ^= set2

print(set1)

実行結果

{2, 4, 5}
{2, 4, 5}


frozenset

frozensetは追加・削除・更新などの操作が不可能(イミュータブル)なセットです。

関数の戻り値として使用する場合など、要素を変更されたくない場合などにfrozensetを使用します。

fset1 = frozenset()
print(fset1)

fset2 = frozenset({1, 2, 3, 4, 5, 3, 2, 4})
print(fset2)

# fset2.add(1) # 実行エラー

実行結果

frozenset()
frozenset({1, 2, 3, 4, 5})


参考文献

Python 公式リファレンス

https://docs.python.org/ja/3.7/index.html

Python-izm

https://www.python-izm.com/

ゲームを作りながら楽しく学べるPythonプログラミング

エキスパートPythonプログラミング

エキスパートPythonプログラミング 改訂2版 (アスキードワンゴ)

エキスパートPythonプログラミング 改訂2版 (アスキードワンゴ)

Python学習メモ - その6

まえがき

最近、Python を勉強し始めたので、その学習メモです。

今回はリスト(List)についてです。


リスト(List)

リストは前回説明したタプルと同様、複数のデータをまとめて扱う場合に使用しますが、タプルとの違いは要素の追加や挿入、削除などが可能なことです。
また、タプルの各要素は変更不可(イミュータブル)ですが、リストの要素は値を編集することができます(ミュータブル)。
そのため、名前の通りTODOリストや顧客リストといった大量かつ編集可能な情報を取り扱う場合によく使用されます。


リスト定義 & アクセス

リストもタプルと同様複数のデータをカンマ( , )で区切りますが、前後に大かっこ「[ ]」をつけることでリストとして認識されます。
リストは、タプルと同様データの各要素へのアクセスは"[i]"で行います。
インデックス i は 0 〜 n-1 (n : 要素数)となります。
※ リストの要素へのアクセスは0ベースインデックスとなります

list_values = [1, 2, 3]

print(list_values)
print(list_values[0])
print(list_values[1])
print(list_values[2])

実行結果

[1, 2, 3]
1
2
3


複数型

タプルと同様、異なる型のデータを混在させることができます。

list_values = [1, 3.14, "list"]

print(list_values)

実行結果

[1, 3.14, 'list']


値の変更

リストでは、指定したインデックスの要素を変更することができます。

list_values = [1, 2, 3]

print(list_values)

list_values[1] = 4

print(list_values)

実行結果

[1, 2, 3]
[1, 4, 3]


空リスト

空(要素数0)のリストを作成したい場合には、「[]」のみを記述します。

list_values = []

print(list_values)

実行結果

[]


要素の追加

appendメソッドを使用することで要素の追加が可能です。

list_values = []
list_values.append(1)
list_values.append(3.14)
list_values.append("list")

print(list_values)

実行結果

[1, 3.14, 'list']


要素の挿入

insertメソッドを使用することで、指定したインデックス位置に要素を挿入することができます。

list_values = []
list_values.append(1)
list_values.append(3.14)
list_values.append("list")

list_values.insert(2, 2 + 3j)

print(list_values)

実行結果

[1, 3.14, (2+3j), 'list']


要素の削除(値検索)

指定した値を検索して要素を削除するには、removeメソッドを使用します。
※ 同じ値の要素が複数存在する場合は、先頭から探して最初に見つかった要素のみが削除されます

list_values = [1, 2, 3, 1, 2, 3]

list_values.remove(3)

print(list_values)

実行結果

[1, 2, 1, 2, 3]


要素の削除(インデックス指定)

指定したインデックスの要素を削除するには、del演算子を使用します。

list_values = [1, 2, 3, 1, 2, 3]

del list_values[1]

print(list_values)

実行結果

[1, 3, 1, 2, 3]


要素の取り出し

popメソッドを使用することで、指定したインデックスの要素を取り出し(取得 & 削除)することができます。

list_values = [1, 2, 3, 1, 2, 3]

pop_value = list_values.pop(2)

print(pop_value)
print(list_values)

実行結果

3
[1, 2, 1, 2, 3]


リストのクリア

リストのすべての要素をクリアするには、clearメソッドを使用します。

list_values = [1, 2, 3, 1, 2, 3]

list_values.clear()

print(list_values)

実行結果

[]


インデックス検索

指定した値を持つ要素のインデックス番号を取得したい場合は、indexメソッドを使用します。
※ 同じ値の要素が複数存在する場合は、先頭から探して最初に見つかった要素のインデックスが返されます

list_values = [1, 2, 3, 1, 2, 3]

index = list_values.index(3)

print(index)

実行結果

2


指定要素数取得

指定した値を持つ要素の数を取得したい場合は、countメソッドを使用します。

list_values = [1, 2, 3, 1, 2, 3]

count = list_values.count(3)

print(count)

実行結果

2


全要素数取得

リスト全体の要素数を取得したい場合はlen関数を使用します。

list_values = [1, 2, 3, 1, 2, 3]

count = len(list_values)

print(count)

実行結果

6


ソート(リスト自体)

リストの要素をソート(並び替え)したい場合はsortメソッドを使用します。

list_values = [2, 5, 1, 3, 4]

list_values.sort()

print(list_values)

実行結果

[1, 2, 3, 4, 5]


ソート(ソートされた新しいリスト)

あるリストに対してソートされた新しいリストを生成したい場合はsorted関数を使用します。

list_values = [2, 5, 1, 3, 4]

soreted_list = sorted(list_values)

print(soreted_list)

実行結果

[1, 2, 3, 4, 5]


コピー

リストの複製を作成したい場合は、copyメソッドを使用します。
Pythonではミュータブルなオブジェクトの代入は参照をコピーします。
例えば、下記のsrc_list_valuesをdst_list_values1に代入していますが、これらは内部的には同じオブジェクトを指しています。
そのため、代入先のdst_list_valuesの値を書き換えたり要素を追加しても、その変更がsrc_list_valuesにも反映されてしまいます。


そういった問題を回避するために、copyメソッドでは、すべての要素を複製した新しいオブジェクトを作成して返します。
これにより、src_list_valuesとは別物のオブジェクトとなるため、値を書き換えたり要素を追加しても、その変更がsrc_list_valuesに反映されなくなります。


なお、copyメソッドは浅いコピー(shallow copy)といって、階層化されたデータのコピーには対応していません。
長くなるのでその辺の話は別の機会にしようと思います。

src_list_values = [1, 2, 3, 4, 5]
dst_list_values1 = src_list_values
dst_list_values2 = src_list_values.copy()

print(src_list_values)
print(dst_list_values1)
print(dst_list_values2)

print()

src_list_values[2] = 10
dst_list_values1[4] = 20
print(src_list_values)
print(dst_list_values1)
print(dst_list_values2)

実行結果

[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

[1, 2, 10, 4, 20]
[1, 2, 10, 4, 20]
[1, 2, 3, 4, 5]


拡張

extendメソッドを使用することで、あるリストに新しいリストを追加(拡張)することができます。

list_values1 = [1, 2, 3]
list_values2 = [4, 5, 6]

list_values1.extend(list_values2)

print(list_values1)

実行結果

[1, 2, 3, 4, 5, 6]


結合

2つ以上のリストを結合した新しいリストを作成したい場合は + 演算子を使用することができます。

list_values1 = [1, 2, 3]
list_values2 = [4, 5, 6]

list_values3 = list_values1 + list_values2

print(list_values3)

実行結果

[1, 2, 3, 4, 5, 6]


存在確認

リストの中に特定の値が含まれているかを確認したい場合は、in演算子を使用します。

list_values = [1, 2, 3]

print(2 in list_values)

実行結果

True


逆に、リストの中に特定の値が含まれていないことを確認したい場合は、not in 演算子を使用します。

list_values = [1, 2, 3]

print(4 not in list_values)

実行結果

True


最大・最小値

リスト内の最大・最小値を求めたい場合は、max関数、min関数を使用します。

list_values = [1, 2, 3]

print(max(list_values))
print(min(list_values))

実行結果

3
1


参考文献

Python 公式リファレンス

https://docs.python.org/ja/3.7/index.html

Python-izm

https://www.python-izm.com/

ゲームを作りながら楽しく学べるPythonプログラミング

エキスパートPythonプログラミング

エキスパートPythonプログラミング 改訂2版 (アスキードワンゴ)

エキスパートPythonプログラミング 改訂2版 (アスキードワンゴ)

Python学習メモ - その5

まえがき

最近、Python を勉強し始めたので、その学習メモです。

今回はタプル(Tuple)についてです。


タプル(Tuple)

プログラミングをしていると、複数のデータをまとめて扱いたいときがあります。
例えば、座標などがそうです。
座標は、x軸とy軸(3次元の場合にはz軸)が合わさって意味のあるのデータになります。
タプルを使うことでそういった複数のデータを扱いやすくなります。

x = 1
y = 2
z = 3

tuple_value = (x, y, z)

print(tuple_value)

実行結果

(1, 2, 3)


タプルは、一度宣言すると値の追加や変更・削除ができません。
こういったデータのことをイミュータブル(不変)であると言います。
そのため、タプルは入力データや計算途中で使われる一時的なデータなど、勝手に改変されたくないデータに使用されることが多いです。


タプル定義 & アクセス

タプルは複数のデータをカンマ( , )で区切ることで定義することができます。
前後のカッコ「( )」はつけなくても良いのですが、わかりやすくするためにつけられることが多いです。
タプルで定義したデータの各要素へのアクセスは"[i]"で行います。
インデックス i は 0 〜 n-1 (n : 要素数)となります。
※ タプルの要素へのアクセスは0ベースインデックスとなります

tuple_values = (1, 2, 3)

print(tuple_values)
print(tuple_values[0])
print(tuple_values[1])
print(tuple_values[2])

実行結果

(1, 2, 3)
1
2
3


複数型

タプルには複数のデータ型の値を混在させることができます。

tuple_values = (1, 3.14, "tuple")

print(tuple_values)
print(tuple_values[0])
print(tuple_values[1])
print(tuple_values[2])

実行結果

(1, 3.14, 'tuple')
1
3.14
tuple


インデックス検索

タプルの各要素から、特定の値が含まれているインデックス番号を取得するには、index()メソッドを使用します。
ただし、同じ値が複数含まれている場合、最初に見つかったインデックス番号が返されます。

tuple_values = (1, 2, 3, 1, 2, 3)

index = tuple_values.index(3)

print(index)

実行結果

2


指定要素数取得

タプルに指定した値の要素がいくつ含まれているかを数えるには、count()メソッドを使用します。

tuple_values = (1, 2, 3, 1, 2, 3)

count = tuple_values.count(3)

print(count)

実行結果

2


全要素数取得

タプルに含まれているすべての要素数を数えたい場合には、len()関数を使用します。

tuple_values = (1, 2, 3, 1, 2, 3)

count = len(tuple_values)

print(count)

実行結果

6


タプル展開

タプルは、1つの変数として定義することもできますが、複数の変数に展開する事もできます。

(x, y) = (1, 2)

print(x, y)

tuple_values = (1, 2)

(x, y) = tuple_values

print(x, y)

実行結果

1 2
1 2


連結

複数のタプルを"+"演算子によって連結することができます。
2つ以上のタプルを"+"演算子で接続すると、連結された新しいタプルが返されます。

tuple1 = (1, 2)
tuple2 = (3, 4)
tuple3 = tuple1 + tuple2
print(tuple3)

実行結果

(1, 2, 3, 4)


参考文献

Python 公式リファレンス

https://docs.python.org/ja/3.7/index.html

Python-izm

https://www.python-izm.com/

ゲームを作りながら楽しく学べるPythonプログラミング

エキスパートPythonプログラミング

エキスパートPythonプログラミング 改訂2版 (アスキードワンゴ)

エキスパートPythonプログラミング 改訂2版 (アスキードワンゴ)