LoginSignup
1
2

More than 5 years have passed since last update.

# C++でプロパティーを実現する方法

Last updated at Posted at 2018-10-16

始めに

C# に慣れちゃうと C++ にもプロパティーが欲しくなる事があり検索するとプロキシとして動くオブジェクトを作る実装例が見受けれます。 軽く眺める限りコード量に見合うメリットが感じられ無いので出来る限り簡潔に書ける方法を考えてみました。

実例

#include <cstddef>
#include <cstdint>
#include <iostream>

struct Foo {
    int one = 1;
    union { // size = 1
        char pad;
        struct {
            auto operator=(int n) -> const int& { // setter
                auto this_ = reinterpret_cast<Foo*>(std::intptr_t(this) - offsetof(Foo, pad));
                return this_->one = n;
            }
            operator int() const { // getter
                auto this_ = reinterpret_cast<Foo*>(std::intptr_t(this) - offsetof(Foo, pad));
                return this_->one;
            }
            char pad;
        } value;
    };
};

int main() {
    Foo f;
    f.value = 2;
    std::cout << f.value << std::endl;
    std::cout << sizeof(f.value) << std::endl;
    return 0;
}

無名の union / struct にプロパティーを実現するメソッドを定義し実際の this 位置は offsetof で求めちゃえば、後はやりたい放題でございます。 1バイト必須になりますが char bool int とか組み込み型を一つ union に入れちゃえば良いですし簡単でしょ? 利用者側のコードを変更する事なくフック出来るので無名実装の方がメリットがあります。

無駄な1バイトは char pad[0]; にすると無くなりますが VS はサイズ 0 を含む型の継承を許さないのでコンパイラベンダー依存にしたくない場合は注意して下さい。

実用的なバージョン

業務で実用出来るような気がしてきたので実用的なコードも考えてみました。

/**
 * @brief プロパティーの入れ物宣言
 * 無名unionに含め位置解決に使用する
 */
#define PROPERTIES() /**/ property<void> property_recognition_

/**
 * @brief プロパティー実装の基底
 * union に入れる事
 */
template <class Base>
struct property {

    using base_type = Base;

    // 危険なので抑制 (union 内でのみ作れる)
    property() = delete;
    property(property&&) = delete;
    void operator=(property&&) = delete;
    auto operator&() = delete;

    /// 本体のポインタを返す
    auto base() noexcept -> base_type* {
        // g++ -Wno-invalid-offsetof
        constexpr auto offset = offset(base_type, property_recognition_);
        return reinterpret_cast<base_type*>(std::intptr_t(this) - offset);
    }

    /// 本体のポインタを返す
    auto base() const noexcept -> const base_type* {
        const_cast<property*>(this)->base();
    }
};

class Foo {
public:
    Foo()
    {}

    union {
        PROPERTIES();
        struct : property<Foo> {
            auto operator=(int n) noexcept -> void {
                base()->value_ = n;
            }
            operator int() const noexcept {
                return base()->value_;
            }          
        } value;
    };

private:
    int value_ = 111;
};

int main() {
    Foo t;
    return t.value;
}

どでしょ?
operator auto とか operator. の出番なんすかねぇ...

最新は多分こちら https://gist.github.com/m5knt/181e889f2fae995a506f1a8464c1e774

参考

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2