2016-04-13 27 views
2

私のフレームワークの一部をPythonからDに翻訳しようとしていて、一つの細部に苦労しています。しかし、まずスコープを広げなければなりません。コンポーネントはORMに似ています。ユーザーはクラスを定義します。このインスタンスはデータベースに永続的に格納されます。ユーザーAPIはできるだけシンプルでなければなりません。たとえば、D Python記述子のようなメタプログラミング?

class Person: 
    identity _id, 
    string name, 
    date birthdate, 
    ... 

class Car: 
    indentity _id, 
    string ident, 
    int power, 
    Person owner 
    ... 

myCar = Car.load(ident="DEAD-BEAF") 
print myCar.power 
print myCar.owner.name 

load-functionは、データベースからインスタンスデータをロードします。しかし、アプリケーションのほとんどが車で動作するので、インスタンスが使用されるまで、車の所有者のローディングは延期されるべきであり、所有者はめったに使用されない。

Pythonでは、Descriptor-Protocolを使用してこの動作を実装できます。私はディスクリプタである "フィールド"クラスのセットを持っています。たとえば、

class StringField(object): 

    def __get__(self,obj,objtype): 
     # get the value from obj 

    def __set__(self,obj,value):      
     # set value in obj 

エンティティクラスには適切なメタクラスがあり、必要な接続が配線されます。ユーザーは、Pythonで定義されています。

class Person(EntityClass):  
    name = StringField() 
    birthdate = DateField() 
    ... 

class Car(EntityClass): 
    ident = StringField() 
    power = IntField() 
    owner = RelationField(Person) 
    ... 

およびクラスを使用しています:

myCar = Car.load(ident="DEAD-BEAF") 
print myCar.power  (#1) 
print myCar.owner.name (#2) 

私から車をロードする場合myCar.powerへの呼び出しが

Car.power.__get__(myCar,Car) 

に展開されているボンネットの下にデータベース、私は所有者IDだけを読み込みます。一つは 所有者に

theowner = myCar.owner 

を使用している場合、私は異なる実装を試みたDへのORMを翻訳データベース

class RelationField(object): 

    def __get__(self,obj,objtype): 
     if not instance in obj.cache: 
      load instance 
      add instance to obj.cache 
     return instance from obj.cache 

から人物のインスタンスを遅延ロードすることができます。シンプルbasetypesにとっては、テンプレートと統一された呼び出し構文と接続詞でのユーザー定義属性(UDA)を使用することは非常に簡単です:

struct Persistent {}; 

save(T)(ref T obj) 
    { 
    ... 
    } 

T load(T)(...) 

class Person 
    { 
    @Persistent string name; 
    @Persistent Date birthday; 
    ... 
    } 

class Car 
    {  
    @Persistent string ident; 
    @Persistent int power; 
    @Persistent Person owner; //??? 
    ... 
    } 

auto myCar = load!Car(...); 
writeln(myCar.power); 
writeln(myCar.owner.name) 

このAPIは、Python-APIと同じくらい簡単ですが、私はどのように見当がつかない所有者の遅延ロードを実装します。私が必要とするのはowner-memberをプロパティ関数で置き換えることですが、コンパイル時のメタプログラミングを使用してこれを行う方法はわかりません。それでどうやってこれをやるの?それとも、慣用的なやり方がありますか?

+0

私はあなたが 'Person'をラップする' Deferred'テンプレートを作ることができたと思います。そこに 'get'関数を作成してロードし、' alias get this'を使って 'owner.get.xyz'の代わりに' owner.xyz'を書くことができます。 – weltensturm

+0

私はそれを試みましたが、値を設定したいので、 'myCar.owner = new Owner(...);'と書く必要があります。だから私は取得し、設定するためにエイリアスを持っている必要があります。 –

+0

テンプレートがクラスではなく構造体にインスタンス化されている場合、opAssignをオーバーライドできるはずですが、別名が構造体で機能するかどうかはわかりません。 – weltensturm

答えて

1

opDispatchを使用できます。プロパティ名はコンパイル時に使用できるため、汎用コードで扱うことができます。または、template行の後にifを使用して、以下のコードのような特殊化を作成することもできます。 addMyHandlerミックスインテンプレートが渡されたのイントロスペクションを使用して、上記opDispatch機能を生成する必要があります。ここ

struct Car { 
    int id; 
    int power; 
    int owner_id; 
    mixin addMyHandlers!Car; 
} 

:ユーザーはこのような何かを書く必要がありますのでopDispatchテンプレートは、ミックスインのテンプレートによって生成することができ

struct Person { 
    int id; 
    string name; 
} 

Person[int] person_by_id; 

struct Car { 
    int id; 
    int power; 
    int owner_id; 

    // https://dlang.org/spec/operatoroverloading.html#dispatch 
    template opDispatch(string property) 
    if (property == "owner") 
    { 
     @property ref Person opDispatch() { 
      return person_by_id[owner_id]; 
     } 
    } 
} 

void main() { 
    auto p = Person(1, "Joe"); 
    person_by_id[p.id] = p; 
    auto c = Car(123, 900, 1); 

    assert(c.owner.name == "Joe"); 
    c.owner.name = "Average Joe"; 

    assert(person_by_id[1].name == "Average Joe"); 
} 

構造体にある。あなたは構造体きれいに保つことを好む場合は、構造体の外の関数を生成し、かつ均一な関数呼び出し構文(UFCS)を利用することができます。

struct Car { 
    int id; 
    int power; 
    int owner_id; 
} 

@property ref Person owner(const ref Car car) { 
    return person_by_id[car.owner_id]; 
} 

このowner機能もミックスインのテンプレートを使用して生成することができますCarのイントロスペクションを使用して、何を生成するかを調べます。 (あなたのPythonコードと同様に)

関連する問題