robot
最新文章(10)
Mqskit 和其它相關工具
CPython 的 GC 二、三事
寫 Mecurial Extension 是件快樂的事!
Mozilla 台灣辨公室徵人啟事
關於 Apple 的兩項專利
core dump 之前的 frame
怎麼發出 beep 聲?
先承認你要找的是奴才吧!
程式碼要清的多乾淨?
FreeBSD 的 Thread-Local Storage 實作
首頁
新編
最新留言
Entries RSS
重要關鍵字(10)
coding (122)
Python (93)
FreeBSD (71)
WEB (61)
URL (48)
hardware (46)
javascript (36)
Linux (34)
blog (30)
C++ (16)
所有關鍵字
新增 URL
動態欄位(屬性)
by thinker
2 Columns
關鍵字:
coding
C++
在 $Python$ 、 Ruby 一類的語言, variable 和 member 是能動態定義的,無需在 compile 階段就定義。這對 programmer 而言,帶來許多的好處。任何人隨時都可為任何物件加上一些屬性,不需要修改原本的 class 。然而,在 $C++$ 或 Java 這類語言,卻無法做到。 template 雖然能以 type 為參數,透過定義增減欄位,以 mix-in 的方式為 object 加上屬性,然而卻必需修改該 mix-in ,以增減屬性,造成很強的 couple 。相關的程式,很容易因為 mix-in 的更動而受影嚮。另一方面, mix-in 只是將修改,從一個 class 移到另一個 class 而已,並沒有真正的解決問題。 {{{#!cpp template<class T> class _data: public T { }; struct mixin { ...... int filed1; char *attr2; // append your attributes here. }; typedef _data<mixin> data; }}} 動態屬性的好處,在於任何 class 只要有需要,都可以為另一個 class 的 instance 加上屬性,以便保留和該 instance 相關的資訊,避免資訊分散。這在程式碼的管理上,提供較佳的方便性和邏輯,也接近人腦的思考方式。 這裡我以 template ,為 $C++$ implement 動態欄位的功能。動態欄位,有點像是附帶的資訊,在通訊上稱為 linkname:piggyback http://en.wikipedia.org/wiki/Piggyback ,我也延用這個名$詞$。附帶的屬性,稱為 piggy (豬玀? :p),而被附著屬性的 object ,稱為 carrier 。 {{{#!cpp #include <typeinfo> #include "common.h" class pg_good { public: pg_good *next; const std::type_info &tp; pg_good(const std::type_info &_tp): tp(_tp) {} virtual ~pg_good() {} virtual pg_good *clone(void) { throw not_implemented_err(); return NULL; } }; template <class T> class cleaner { public: typedef void (*cleaner_t)(T &d); static void dummy(T &d) {} static void del(T &d) { delete d; } }; template <class T, typename cleaner<T>::cleaner_t clr = cleaner<T>::dummy> class pg_good_val: public pg_good { T data; public: pg_good_val(T d): pg_good(typeid(T)), data(d) {} ~pg_good_val() { clr(data); } T &get_data(void) { return data; } pg_good *clone(void) { return new pg_good_val<T, clr>(data); } }; class piggy_base { pg_good *first; void clean_goods(void); void clone_goods(pg_good *first); public: piggy_base(void) { first = NULL; } piggy_base(const piggy_base &a_piggy) { first = NULL; clone_goods(a_piggy.first); } ~piggy_base() { clean_goods(); } void carrier_add(pg_good *a_pg_good) { a_pg_good->next = first; first = a_pg_good; } pg_good *piggy_find(const std::type_info &tp); piggy_base &operator =(const piggy_base &a_piggy) { clean_goods(); clone_goods(a_piggy.first); return *this; } }; template<class T> class piggy_carrier: public piggy_base, public T { public: piggy_carrier(void): piggy_base(), T() {} piggy_carrier(const piggy_carrier &ca): piggy_base(ca), T(ca) {} piggy_carrier &operator =(const piggy_carrier &a_carrier) { piggy_base &base = *this; T &d = *this; base = a_carrier; d = a_carrier; return *this; } }; template<class T, typename cleaner<T>::cleaner_t clr> void add(piggy_base &ca, T d) { pg_good_val<T, clr> *a_pg_good = new pg_good_val<T>(d); ca.carrier_add(a_pg_good); } template<class T> void add(piggy_base &ca, T d) { add<T, cleaner<T>::dummy>(ca, d); } template<class T> T &visit(piggy_base &ca) { pg_good_val<T> *a_pg_good; a_pg_good = static_cast<pg_good_val<T> *>(ca.piggy_find(typeid(T))); if(a_pg_good == NULL) throw key_err(); return a_pg_good->get_data(); } }}} common.h 的內容如下 {{{#!cpp class err { public: char *name; char *fname; int lineno; }; #define MAKE_ERR(_name) class _name: public err { \ public: \ _name(char *_fname="", int _lineno=-1) { name = #_name; fname = _fname; lineno = _lineno; } \ } MAKE_ERR(value_err); MAKE_ERR(key_err); MAKE_ERR(index_err); MAKE_ERR(runtime_err); MAKE_ERR(assert_err); MAKE_ERR(proto_err); MAKE_ERR(not_implemented_err); }}} 這個 piggyback 是透過 RTTI 資訊,找出對映的屬性。在沒有 RTTI 的環境,必需換成其它的 ID ,如某個固定名稱的 static member 的位址。透過 template 的功能, compiler 能自動提供正確的 type ,因此在使用上極為方便。下面是一個使用$範例$。 {{{#!cpp class $test$ { }; class data1 { public: int val; data1(int p): val(p) {} }; class data2 { public: int val; data2(int p): val(p) {} }; typedef piggy_carrier<$test$> $test$_pc; }}} 上例透過 piggy_carrier 將 $test$ 包裝成一個 carrier ,以提供動態欄位的功能。 $test$_pc 實際上是繼承 $test$ 所有的屬性和 method ,再加上 carrier 所必需有的 method 和屬性。 下面是一段測試程式 {{{#!cpp $test$_pc t; $test$_pc t2; add(t, data1(3)); // add a dynamic attribute add(t, data2(55)); // add another dynamic attribute ensure("visit<data1>", visit<data1>(t).val == 3); ensure("visit<data2>", visit<data2>(t).val == 55); t2 = t; // copy dynamic attributes between carriers ensure("visit<data1>(t2)", visit<data1>(t2).val == 3); ensure("visit<data2>(t2)", visit<data2>(t2).val == 55); }}} 動態屬性,是以資料的 type 為存取的 key 。因此,當你相同的資料,必需以不同名稱存取時,可透過繼承該 class ,定義不同的 sub-class ,作為存取的 key 。例如: {{{ #define PG_ATTR(name, dtype) \ class name : public dtype { \ public: \ name(const dtype &d): dtype(d) {} \ } PG_ATTR(data1_1, data1); PG_ATTR(data1_2, data1); ..... add(t, data1_2(data(33))); ... foo = visit<data1_2>(t).val; }}} 這裡定義了 data1_1 和 data_2 ,作為動態屬性的名稱。 有了動態屬性,當您定義了 class A 時,除了和 class A 功能相關的屬性,就只需將 class 包裝成 piggy_carrier ,不再考慮是否有其它屬性必需加進來。其它 class 或模組,可透過 piggyback 的方式加上所需的屬性,而不需修改 class A 。 == 下載 == * linkname:[完整檔案] attach:piggy.tar.gz
最後更新時間: 2007-03-11 23:06:12 CST |
引用
查詢:
COMMENTS: