非専門的シンギュラリティー研究所

無限に動き続けるシステムを表す方法を AI なども使って考えていきます。

コンパイラ(4)

C# のコードとして構文を書くとき、演算子再定義のほかに以下のような機能を使っていたので ChatGPT で調べてみます。これを C++ で実現するには、同じようにはできないのでやり方を考えないといけないようです。

C# のプロパティで、本体が単一の式であるものについて説明してください

と入力すると以下のような答えが返されました。

C# のプロパティには「式本体(expression-bodied)プロパティ」という簡潔な記述方法があります。これは、本体が単一の式である場合に `get` アクセサーを簡略化して記述できる構文です。

基本構文

型 プロパティ名 => 式;

これは読み取り専用プロパティ(`get` だけを持つ)になります。

public class Circle
{
    public double Radius { get; set; }

    // 面積を計算する読み取り専用プロパティ
    public double Area => Math.PI * Radius * Radius;
}

この `Area` プロパティは、内部的には以下と同等です:

public double Area
{
    get { return Math.PI * Radius * Radius; }
}

式本体のプロパティを使うメリット

  • コードが簡潔になる
  • 単純な計算や値の返却などに最適
  • クラスの中で定義の可読性が向上する

注意点

  • `set` を持つプロパティには使えません(C# 9.0 以降は、`init` 付きの式本体プロパティも可能です)。
  • 複雑なロジックを含む場合は、通常の `get`/`set` ブロックを使うべきです。

C# のバージョンによる違い

  • C# 6.0 以降で利用可能(式本体のメンバーが導入)
  • C# 7.0 以降で式本体の `get`/`set` の両方に対応(ただし、それぞれで明示する必要がある)
public string Name
{
    get => _name;
    set => _name = value;
}

何か具体的なプロパティについて例を見たい場合は教えてください。

このような機能は C++ にはありますか

と入力すると以下のような答えが返されました。

C# のような「式本体プロパティ(expression-bodied property)」のような構文は、C++ には標準では存在しませんが、似たような効果を持つ記述はできます。

C++ における代替方法

C++ では、通常の メンバー関数 または 関数の定義 を「1行の式」で書くことで、同様のシンプルさを実現できます。

例:C# の式本体プロパティ

public double Area => Math.PI * Radius * Radius;

C++ での類似の書き方

class Circle {
    double radius;

public:
    Circle(double r) : radius(r) {}

    // 読み取り専用「プロパティ」の代わりに、関数を使う
    double area() const { return 3.141592653589793 * radius * radius; }
};

または、C++14 以降なら「関数本体を単一式で」定義できます:

double area() const { return M_PI * radius * radius; }

C++20 以降なら `consteval` や `constexpr` も活用可能です:

constexpr double area() const noexcept {
    return M_PI * radius * radius;
}

C++ にはないもの

  • C# のような `get` プロパティとして `プロパティ名 => 式;` のような専用の構文
  • 自動的に「変数のように見えるが実は関数」という プロパティの構文糖衣

🔄 ただし擬似的に実現する方法(プロパティ風)

C++ では `operator` オーバーロードや getter 関数を使って、プロパティ風に書くことも可能です。

例:読み取り専用プロパティ風

class Circle {
    double radius;

public:
    Circle(double r) : radius(r) {}

    struct AreaProxy {
        const Circle& c;
        operator double() const { return 3.14159 * c.radius * c.radius; }
    };

    AreaProxy Area() const { return AreaProxy{ *this }; }
};

使用側:

Circle c(5);
double a = c.Area(); // 変数のように扱える

✅ 結論

特徴 C# C++
式本体プロパティ `double Area => 式;` `double area() const { return 式; }`
プロパティ構文 あり(`get; set;`) なし(関数またはプロキシで代用)
自動プロパティ `public int Value { get; set; }` `int value;`(直接公開 or getter/setter)

必要であれば、C++ で「get/set をプロパティっぽく見せるテンプレート」なども紹介できます。ご希望があればお知らせください。

BNF 風に構文を記述するために使いたいのですが、良い方法はありますか

と入力すると以下のような答えが返されました。

C++BNF 風に構文を記述してプロパティのようなものを扱いたい場合、BNF の要素(非終端記号、終端記号、選択、繰り返しなど)をC++の構造に対応させて表現する方法が考えられます。

ここでは次の2点を意識して整理します:

1. BNF 風の構文の表現
2. C++ のプロパティ相当のものを組み込む方法

BNF 風の構文記述の例

まず、BNF で「式本体プロパティ」を記述すると以下のようになります:

<property-declaration> ::= <type> <identifier> "=>" <expression> ";"

これを C++BNF構文ルールを記述するためのクラスや構造体で表現すると、以下のような形が考えられます。

C++BNF風構文を扱う方法

方法A: 構文ルールを構造体でモデル化

struct PropertyDeclaration {
    std::string type;
    std::string identifier;
    std::string expression;

    std::string to_string() const {
        return type + " " + identifier + " => " + expression + ";";
    }
};
使用例:
PropertyDeclaration pd{
    "double",
    "Area",
    "PI * Radius * Radius"
};

std::cout << pd.to_string() << std::endl;
// 出力: double Area => PI * Radius * Radius;

方法B: 再帰構造を持つBNF記述 DSLドメイン特化言語)として作る

BNF自体の構文ツリーを表すクラス群を定義する方法です:

struct Rule {
    std::string lhs;
    std::vector<std::string> rhs;
};

Rule property_declaration = {
    "property-declaration",
    {"type", "identifier", "=>", "expression", ";"}
};

または、AST(抽象構文木)形式にすることで解析・出力・検証がしやすくなります。

方法C: DSLっぽく書くテンプレート構文

もっと踏み込んで、テンプレートや演算子オーバーロードを駆使すれば、BNFDSLを実現することも可能です:

auto rule = type_ + identifier + "=>" + expression + ";";

これは Boost.Spirit などのパーサーコンビネータライブラリで使われる記法に似ています。

C++ で「プロパティ」的な表現を取り扱いたい場合

BNF上の `` をパースして実際の C++ コードや式に変換したい場合は、以下の2ステップが必要です:

1. BNF ルールに基づいた構文解析(パーサー)
2. AST やクラス定義へのマッピング

そのためには次のような構成が有効です:

struct Expression {
    virtual std::string emit() const = 0;
};

struct Property : Expression {
    std::string type;
    std::string name;
    std::string expression;

    std::string emit() const override {
        return type + " " + name + "() const { return " + expression + "; }";
    }
};

使用例:

Property area = {"double", "Area", "PI * Radius * Radius"};
std::cout << area.emit() << std::endl;
// 出力: double Area() const { return PI * Radius * Radius; }

補足:BNF をベースにした C++ 向けパーサー生成器

もし構文解析を目的としている場合、以下のライブラリを使うのも有効です:

ツール 説明
Boost.Spirit BNF風の記法でC++内に直接構文規則を書ける強力なパーサー
ANTLR 文法をBNFライクに定義してC++などのコードを生成可能
PEGTL Parsing Expression GrammarベースのC++テンプレートライブラリ

結論

  • C#の式本体プロパティをBNFで記述することは可能
  • それをC++で取り扱うには「構文規則をC++構造にマッピングする」必要がある
  • DSL風にするか、パーサー/AST的に設計するかは用途次第
  • `Boost.Spirit` のようなライブラリを使えば、BNF構文をそのままコードとして扱える

ご希望に応じて、「BNF からパーサーを作成」「C++ に式本体プロパティを模倣する機構を作成」など、さらに具体的なコードをご提案できます。必要であればお知らせください。