1 /**
2  * The iz property descriptor system.
3  */
4 module iz.properties;
5 
6 import
7     std.traits;
8 import
9     iz.memory, iz.types, iz.containers, iz.rtti, iz.enumset;
10 
11 version(unittest) import std.stdio;
12 
13 /**
14  * Used as a generic property descriptor when Rtti are used to detect
15  * the target type.
16  */
17 alias GenericDescriptor = PropDescriptor!int;
18 
19 /**
20  * Describes a property declared in an aggregate.
21  *
22  * A property is described by a name, a setter and a getter. Several constructors
23  * allow to define the descriptor using a setter, a getter but also a pointer to
24  * the targeted field. Another useful member is the pointer to the Rtti structure
25  * matching to the descriptor specialization.
26  */
27 struct PropDescriptor(T)
28 {
29     @disable this(this);
30 
31 public:
32 
33     static if (!is(T == struct))
34     {
35         /// setter proptotype
36         alias PropSetter = void delegate(T value);
37         /// getter prototype
38         alias PropGetter = T delegate();
39         /// alternative setter prototype.
40         alias PropSetterConst = void delegate(const T value);
41     }
42     else
43     {
44         /// setter proptotype
45         alias PropSetter = void delegate(T value);
46         /// getter prototype
47         alias PropGetter = ref T delegate();
48         /// alternative setter prototype.
49         alias PropSetterConst = void delegate(const T value);
50     }
51 
52 package:
53 
54     PropHints _hints;
55 
56 private:
57 
58     PropSetter _setter;
59     PropGetter _getter;
60     Object _declarator;
61     const(Rtti)* _rtti;
62 
63     T* _directPtr;
64 
65     Array!(char) _name;
66 
67     void cleanup()
68     {
69         _directPtr = null;
70         _setter = null;
71         _getter = null;
72         _declarator = null;
73         _name = _name.init;
74     }
75 
76     // pseudo setter internally used when a T is directly written.
77     void internalSetter(T value)
78     {
79         alias TT = Unqual!T;
80         T current = getter()();
81         if (value != current)
82             *cast(TT*) _directPtr = value;
83     }
84 
85     // pseudo getter internally used when a T is directly read
86     static if (is(T==struct))
87         ref T internalGetter()
88     {
89         return *_directPtr;
90     }
91     else
92         T internalGetter()
93     {
94         return *_directPtr;
95     }
96 
97 public:
98 
99 
100 // constructors ---------------------------------------------------------------+
101 
102     /**
103      * Constructs a property descriptor from a PropSetter and a PropGetter.
104      */
105     this(PropSetter aSetter, PropGetter aGetter, string aName = "")
106     in
107     {
108         assert(aSetter);
109         assert(aGetter);
110     }
111     body
112     {
113         define(aSetter, aGetter, aName);
114     }
115 
116     /**
117      * Constructs a property descriptor from a PropSetterConst and a PropGetter.
118      */
119     static if (!is(T == const))
120     this(PropSetterConst aSetter, PropGetter aGetter, string aName = "")
121     in
122     {
123         assert(aSetter);
124         assert(aGetter);
125     }
126     body
127     {
128         define(cast(PropSetter)aSetter, aGetter,aName);
129     }
130 
131     /**
132      * Constructs a property descriptor from a PropSetter and as getter
133      * a pointer to a variable.
134      */
135     this(PropSetter aSetter, T* aSourceData, string aName = "")
136     in
137     {
138         assert(aSetter);
139         assert(aSourceData);
140     }
141     body
142     {
143         define(aSetter, aSourceData, aName);
144     }
145 
146     /**
147      * Constructs a property descriptor from a pointer to a variable used as
148      * a setter and getter.
149      */
150     this(T* aData, string aName = "")
151     in
152     {
153         assert(aData);
154     }
155     body
156     {
157         define(aData, aName);
158     }
159 
160     ~this()
161     {
162         destruct(_name);
163     }
164 // ---- 
165 // define all the members -----------------------------------------------------+
166 
167     /**
168      * Defines a property descriptor from a PropSetter and a PropGetter.
169      */
170     void define(PropSetter aSetter, PropGetter aGetter, string aName = "")
171     {
172         cleanup;
173         _rtti = getRtti!T;
174         setter(aSetter);
175         getter(aGetter);
176         if (aName != "")
177             name(aName);
178         _declarator = cast(Object) aSetter.ptr;
179     }
180 
181     /**
182      * Defines a property descriptor from a PropSetter and as getter
183      * a pointer to a variable.
184      */
185     void define(PropSetter aSetter, T* aSourceData, string aName = "")
186     {
187         cleanup;
188         _rtti = getRtti!T;
189         setter(aSetter);
190         setDirectSource(aSourceData);
191         if (aName != "")
192             name(aName);
193         _declarator = cast(Object) aSetter.ptr;
194     }
195 
196     /**
197      * Defines a property descriptor from a pointer to a variable used as
198      * a setter and getter.
199      */
200     void define(T* aData, string aName = "", Object aDeclarator = null)
201     {
202         cleanup;
203         _rtti = getRtti!T;
204         setDirectSource(aData);
205         setDirectTarget(aData);
206         if (aName != "")
207             name(aName);
208         _declarator = aDeclarator;
209     }
210 // ----
211 // setter ---------------------------------------------------------------------+
212 
213     /**
214      * Sets the property setter using a standard method.
215      */
216     void setter(PropSetter value)
217     {
218         _setter = value;
219         _declarator = cast(Object) value.ptr;
220     }
221 
222     /// ditto
223     PropSetter setter(){return _setter;}
224 
225     /**
226      * Sets the property setter using a pointer to a variable
227      */
228     void setDirectTarget(T* location)
229     {
230         _directPtr = location;
231         _setter = &internalSetter;
232     }
233     /**
234      * Sets the property value
235      */
236     void set(T value) {_setter(value);}
237 // ---- 
238 // getter ---------------------------------------------------------------------+
239 
240     /**
241      * Sets the property getter using a standard method.
242      */
243     void getter(PropGetter value)
244     {
245         _getter = value;
246         _declarator = cast(Object) value.ptr;
247     }
248 
249     /// ditto
250     PropGetter getter(){return _getter;}
251 
252     /**
253      * Sets the property getter using a pointer to a variable
254      */
255     void setDirectSource(T* value)
256     {
257         _directPtr = value;
258         _getter = &internalGetter;
259     }
260 
261     /**
262      * Gets the property value
263      */
264     T get(){return _getter();}
265 // ----     
266 // misc -----------------------------------------------------------------------+
267 
268     /**
269      * Returns this descriptor casted as pointer to a GenericDescriptor.
270      */
271     PropDescriptor!int* genericDescriptor()
272     {
273         return cast(PropDescriptor!int*) &this;
274     }
275 
276     /**
277      * Returns this descriptor casted as pointer to a descriptor of the type
278      * given by template parameter.
279      */
280     PropDescriptor!A* as(A)()
281     {
282         return cast(PropDescriptor!A*) &this;
283     }
284 
285     deprecated alias typedAs = as;
286 
287     /**
288      * Sets of gets the string used to identify the property
289      */
290     void name(const(char)[] value)
291     {
292         _name = value;
293     }
294 
295     /// ditto
296     ref const(Array!char) name()
297     {
298         return _name;
299     }
300 
301     /**
302      * The object that declares this property.
303      * When really needed, this value is set automatically.
304      */
305     void declarator(Object value)
306     {
307         _declarator = value;
308     }
309 
310     /// ditto
311     Object declarator(){return _declarator;}
312 
313     /**
314      * Returns the RuntimeTypeInfo struct for the property type.
315      */
316     const(Rtti*) rtti(){return _rtti;}
317 
318     /**
319      * Returns the hints for this property.
320      */
321     ref PropHints hints(){return _hints;}
322 
323 // ----        
324 
325 }
326 ///
327 unittest
328 {
329     static struct Foo
330     {
331         private int _value = 1;
332         PropDescriptor!int valueDescriptor;
333 
334         void value(int v){_value = v;}
335         int value(){return _value;}
336     }
337 
338     Foo foo;
339     // defines the property using the setter and the getter.
340     foo.valueDescriptor.define(&foo.value, &foo.value, "foo.value");
341     // defines the property using the setter and a pointer to the field.
342     foo.valueDescriptor.define(&foo.value, &foo._value, "foo.value");
343     // .get and .set allow to access the property value
344     foo.valueDescriptor.set(2);
345     assert(foo.valueDescriptor.get == 2);
346     // getter() and setter() too but they are used to set/get the delegates.
347     foo.valueDescriptor.setter()(1);
348     assert(foo.valueDescriptor.getter()() == 1);
349     // a descriptor has a fixed size, whatever is it's specialization,
350     // that allows to cast safely to any other descriptor type.
351     PropDescriptor!float* fpDescr = cast(PropDescriptor!float*) &foo.valueDescriptor;
352     // that's why the halper "as" is provided to cast
353     PropDescriptor!byte* byteDescr = fpDescr.as!byte;
354     // and the actual type can be retrieved with the rtti
355     assert(fpDescr.rtti is getRtti!int);
356     assert(byteDescr.rtti is getRtti!int);
357 }
358 
359 unittest
360 {
361     class A
362     {
363         private int fi;
364         int i(){return fi;}
365         void i(in int aValue){fi = aValue;}
366     }
367     struct Si{uint f,r,e;}
368     class B
369     {
370         private Si fi;
371         ref Si i(){return fi;}
372         void i(Si aValue){fi = aValue;}
373     }
374 
375     auto a = construct!A;
376     auto descrAi = PropDescriptor!int(&a.i,&a.i,"I");
377     descrAi.setter()(5);
378     assert(a.i == 5);
379     assert(a.i == descrAi.getter()());
380     assert(descrAi.declarator is a);
381 
382     auto refval = Si(1,2,333);
383     auto b = construct!B;
384     auto descrBi = PropDescriptor!(Si)(&b.i,&b.i,"I");
385     descrBi.setter()(refval);
386     assert(b.i.e == 333);
387     assert(b.i.e == descrBi.getter()().e);
388 
389     auto descrSi0 = PropDescriptor!(Si)(&b.i, &b.fi);
390     auto descrSi1 = PropDescriptor!(Si)(&b.fi);
391     assert(descrSi0.get() == descrSi1.get());
392     assert(descrSi0.genericDescriptor.rtti is descrSi0.rtti);
393 
394     auto refval0 = Si(1,2,3);
395     descrSi1.set(refval0);
396 
397     destructEach(a,b);
398 }
399 
400 unittest
401 {
402     // the key that allows "safe" cast using iz.types.RunTimeTypeInfo
403     static assert((PropDescriptor!(int)).sizeof == (PropDescriptor!(ubyte[])).sizeof);
404     static assert((PropDescriptor!(string)).sizeof == (PropDescriptor!(ubyte[][][][])).sizeof);
405 }
406 
407 /**
408  * Allows to get the original $(D this) of a struct, for example when
409  * passed from a $(D ref) getter function.
410  */
411 void* getThis(T)(ref T t)
412 if (is(T == struct))
413 {
414     return (cast(void*) &t);
415 }
416 ///
417 unittest
418 {
419     static struct Bar {uint[64] ui;}
420     class Foo
421     {
422         Bar bar;
423         ref Bar barRef(){return bar;}
424         Bar barMov(){return bar;}
425     }
426 
427     Foo foo = construct!Foo;
428     assert(&foo.bar == foo.barRef.getThis);
429     Bar b = foo.barRef;
430     assert(&foo.bar != &b);
431     destruct(foo);
432 }
433 
434 /// designed to annotate a property setter, e.g @Set
435 enum Set;
436 /// designed to annotate a property getter, e.g @Get
437 enum Get;
438 /// designed to annotate a field used as a property without accessors.
439 enum SetGet;
440 /// ditto
441 alias GetSet = SetGet;
442 /// designed to cancel the effect of @Set and @Get in the ancestor classes.
443 enum HideSet;
444 /// ditto
445 enum HideGet;
446 
447 /**
448  * Enumerates the property hints.
449  *
450  * Hints can be attributed to a property. They may be used or not,
451  * depending on the context.
452  */
453 enum PropHint
454 {
455     /**
456      * Indicates that a special behavior should be adopted
457      * depending on if the value is equal or not the initializer.
458      */
459     initCare,
460     /**
461      * Indicates that a property should be considered read-only,
462      * even if a setter is detected.
463      */
464     dontSet,
465     /**
466      * Indicates that a property should be considered write-only,
467      * even if a getter is detected.
468      */
469     dontGet,
470     /**
471      * Indicates that a property shouldn't be used anymore.
472      */
473     obsolete
474 }
475 
476 /**
477  * PropHints can be used to set the hints attributed to a property,
478  * using the UDA syntax: $(D @PropHints(...)).
479  */
480 alias PropHints = EnumSet!(PropHint, Set8);
481 
482 /**
483  * When mixed in an agregate this generates a property. 
484  * This property is detectable by a PropertyPublisher.
485  *
486  * Params:
487  *      T = The type of the property.
488  *      propName = The name of the property.
489  *      propField = The identifier that matches the target field.
490  *
491  * Returns:
492  *      A sring to mixin.
493  */
494 deprecated("fields can be annotated with @GetSet instead")
495 string genPropFromField(T, string propName, string propField)()
496 {
497     return
498     "@Set void " ~ propName ~ "(" ~ T.stringof ~ " aValue)" ~
499     "{ " ~ propField ~ " = aValue;} " ~
500     "@Get " ~ T.stringof ~ " " ~ propName ~
501     "(){ return " ~ propField ~ ";}" ;
502 }
503 
504 private string genStandardPropDescriptors()
505 {
506     string result;
507     foreach(T; BasicRtTypes[1..$])
508     {
509         result ~= ("public alias " ~ T.stringof ~ "Prop = PropDescriptor!(" ~
510             T.stringof ~ ")" ~ "; ");
511     }
512     return result;
513 }
514 
515 /// Property descriptors for the types defined in the iz.types.BasicTypes aliases.
516 mixin(genStandardPropDescriptors);
517 
518 
519 /**
520  * The PropertyPublisher interface allows a class to publish a collection
521  * of properties described using the PropDescriptor format.
522  *
523  * The methods don't have to be implemented by hand as it's automatically done 
524  * when the PropertyPusblisherImpl template is mixed in a class.
525  *
526  * Inspiration:
527  * The semantic used for this interface is inspired by the Object-Pascal
528  * "published" protection attribute. In pascal, "published" causes the
529  * member (called a property) to have the matching RTTI emitted. They
530  * are used to stream objects, to build IDE inspectors, bindings list, etc.
531  *
532  * This interface (as well as its default implementation) reproduces a similar
533  * system. Instead of "published", there are anotations, instead of the RTTI
534  * tree structure there is an array of PropDescriptor.
535  */
536 interface PropertyPublisher
537 {
538     /**
539      * Returns the count of descriptor this class publishes.
540      */
541     size_t publicationCount();
542     /**
543      * Returns a pointer to a descriptor according to its name.
544      * Similar to the publication() function template excepted that the
545      * result type has not to be specified.
546      */
547     GenericDescriptor* publicationFromName(const(char)[] name);
548     /**
549      * Returns a pointer the index-th descriptor.
550      * Index must be within the [0 .. publicationCount] range.
551      */
552     GenericDescriptor* publicationFromIndex(size_t index);
553     /**
554      * Pointer to the object that has created the descriptor leading to this
555      * PropertyPublisher instance.
556      */
557     Object declarator(); //acquirer
558     /// ditto
559     void declarator(Object value);
560 }
561 ///
562 unittest
563 {
564     import iz.streams;
565     static class StuffPublisher: PropertyPublisher
566     {
567         // implements the interface as well as other usefull functions.
568         mixin PropertyPublisherImpl;
569 
570         protected:
571             @SetGet char[] _name = "Fantomas".dup;
572             @SetGet ubyte _age = 33;
573             MemoryStream _opaque;
574 
575         public:
576             this()
577             {
578                 // scans the stuff anotated with @GetSet, @Set, @Get
579                 collectPublications!StuffPublisher;
580                 _opaque = construct!MemoryStream;
581             }
582             ~this() {destruct(_opaque);}
583 
584             @Set void opaque(Stream stream)
585             {
586                 _opaque.loadFromStream(stream);
587             }
588             @Get Stream opaque()
589             {
590                 return _opaque;
591             }
592     }
593 
594     StuffPublisher stuffPublisher = construct!StuffPublisher;
595     // 3 publications are available: name, age and opaque.
596     // they will be handled automatically when binding or serializing.
597     assert(stuffPublisher.publicationCount == 3);
598     // One way to access the publications
599     assert(stuffPublisher.publication!(char[])("name").get == "Fantomas");
600     destruct(stuffPublisher);
601 }
602 
603 /**
604  * Returns true if the argument is a property publisher.
605  */
606 bool isPropertyPublisher(T)()
607 {
608     bool result = true;
609     static if (is(T : PropertyPublisher))
610         return result;
611     else
612     {
613         foreach(interfaceFun;__traits(allMembers, PropertyPublisher))
614         static if (!__traits(hasMember, T, interfaceFun))
615         {
616             result = false;
617             break;
618         }
619         return result;
620     }
621 }
622 
623 ///ditto
624 bool isPropertyPublisher(Object o)
625 {
626     return (cast(PropertyPublisher) o) !is null;
627 }
628 
629 unittest
630 {
631     struct Foo{mixin PropertyPublisherImpl;}
632     class Bar{mixin PropertyPublisherImpl;}
633     class Baz: PropertyPublisher {mixin PropertyPublisherImpl;}
634     static assert(isPropertyPublisher!Foo);
635     static assert(isPropertyPublisher!Bar);
636     static assert(isPropertyPublisher!Baz);
637     auto baz = new Baz;
638     assert( baz.isPropertyPublisher);
639 }
640 
641 /**
642  * Default implementation of a PropertyPublisher.
643  *
644  * When mixed in an aggregate type, two analyzers can be used to create
645  * automatically the PropDescriptors that match the setter and getter pairs
646  * anotated with @Set and @Get or that match the fields annotated with @SetGet.
647  *
648  * The analyzers are usually called in this(). The template has to be mixed in
649  * each class generation that introduces new annotated properties.
650  *
651  * The analyzers, propCollectorGetPairs() and propCollectorGetFields(), are
652  * function templates that must be instantiated with the type they have
653  * to scan (typeof(this)). The two analyzers can be called with a
654  * third function template: collectPublications().
655  */
656 mixin template PropertyPublisherImpl()
657 {
658     /**
659      * Contains the list of PropDesrcriptors created by the analyzers.
660      * Even if it's possible to access directly the array, it's safer to use
661      * ($D publication()), ($D publicationFromName()) and ($D publicationFromIndex()).
662      */
663     static if (!__traits(hasMember, typeof(this), "_publishedDescriptors"))
664     {
665         import iz.containers: Array;
666         protected Array!(void*) _publishedDescriptors;
667     }
668 
669     static if (!__traits(hasMember, typeof(this), "clearDescriptors"))
670     {
671         /**
672          * Destructs each property descriptor and empties their container.
673          * After the call new descriptors can still be added.
674          */
675         pragma(inline, false)
676         public final void clearDescriptors()
677         {
678             if (_publishedDescriptors.length)
679             {
680                 import iz.memory: destruct;
681                 foreach(ptr; _publishedDescriptors)
682                     if (ptr) destruct(cast(GenericDescriptor*) ptr);
683             }
684             _publishedDescriptors.length = 0;
685         }
686 
687         ~this()
688         {
689             clearDescriptors;
690             import iz.memory: destruct;
691             destruct(_publishedDescriptors);
692         }
693     }
694 
695     static if (!__traits(hasMember, typeof(this), "_declarator"))
696     protected Object _declarator;
697 
698 // virtual methods or PropDescriptorCollection methods
699 //
700 // static if: the template injects some virtual methods that don't need
701 // to be overriden
702 // oror Base: because it looks like the interface makes the members
703 // detectable even if not yet implemented.
704 
705     import std.traits: BaseClassesTuple;
706     alias ToT = typeof(this);
707     // descendant already implements the interface
708     enum BaseHas = is(BaseClassesTuple!ToT[0] : PropertyPublisher);
709     enum HasItf = is(ToT : PropertyPublisher);
710     // interface must be implemented from this generation, even if methods detected
711     enum Base = HasItf & (!BaseHas);
712 
713     /// see PropertyPublisher
714     static if (!__traits(hasMember, ToT, "declarator") || Base)
715     public Object declarator() {return _declarator;}
716 
717     /// ditto
718     static if (!__traits(hasMember, ToT, "declarator") || Base)
719     pragma(inline, false)
720     public void declarator(Object value) {_declarator = value;}
721 
722     /// see PropertyPublisher
723     static if (!__traits(hasMember, ToT, "publicationCount") || Base)
724     pragma(inline, false)
725     public size_t publicationCount() {return _publishedDescriptors.length;}
726 
727     /// see PropertyPublisher
728     static if (!__traits(hasMember, ToT, "publicationFromName") || Base)
729     pragma(inline, false)
730     public GenericDescriptor* publicationFromName(const(char)[] name)
731     {return publication!int(name);}
732 
733     /// see PropertyPublisher
734     static if (!__traits(hasMember, ToT, "publicationFromIndex") || Base)
735     pragma(inline, false)
736     public GenericDescriptor* publicationFromIndex(size_t index)
737     {return cast(GenericDescriptor*) _publishedDescriptors[index];}
738 
739 // templates: no problem with overrides, instantiated according to class This or That
740 
741     /**
742      * Returns a pointer to a descriptor according to its name.
743      * Params:
744      *      T = The type of the property.
745      *      name = The identifier used for the setter and the getter.
746      *      createIfMissing = When set to true, the result is never null.
747      * Returns:
748      *      Null if the operation fails otherwise a pointer to a PropDescriptor!T.
749      */
750     pragma(inline, false)
751     public PropDescriptor!T* publication(T)(const(char)[] name, bool createIfMissing = false)
752     {
753         PropDescriptor!T* descr;
754         foreach(immutable i; 0 .. _publishedDescriptors.length)
755         {
756             auto maybe = cast(PropDescriptor!T*) _publishedDescriptors[i];
757             if (maybe.name == name)
758             {
759                 descr = maybe;
760                 break;
761             }
762         }
763         if (createIfMissing && !descr)
764         {
765             import iz.memory: construct;
766             _publishedDescriptors ~= construct!(PropDescriptor!T);
767             (cast(typeof(descr))_publishedDescriptors[$-1]).name = name;
768             descr = cast(typeof(descr)) _publishedDescriptors[$-1];
769         }
770         return descr;
771     }
772 
773     /**
774      * Performs all the possible analysis.
775      */
776     pragma(inline, false)
777     public void collectPublications(T)()
778     {
779         collectPublicationsFromPairs!T;
780         collectPublicationsFromFields!T;
781     }
782 
783     /**
784      * Creates the properties descriptors for each field annotated with @SetGet.
785      *
786      * If the field identifier starts with '_', 'f' or 'F' then the descriptor
787      * .name member excludes this prefix, otherwise the descriptor .name is
788      * identical.
789      */
790     pragma(inline, false)
791     public void collectPublicationsFromFields(T)()
792     {
793         import iz.types: ScopedReachability;
794         import std.traits: isCallable, isDelegate, isFunctionPointer, getUDAs;
795         import iz.rtti: Rtti, getRtti;
796 
797         bool isFieldPrefix(char c)
798         {return c == '_' || c == 'f' || c == 'F';}
799         enum getStuff = q{__traits(getMember, T, member)};
800 
801         mixin ScopedReachability;
802         foreach (member; __traits(derivedMembers, T))
803         static if (member != "__dtor" && member != "__ctor")
804         static if (isMemberReachable!(T, member))
805         static if (is(typeof(mixin(getStuff))))
806         static if (!isCallable!(mixin(getStuff)) || isDelegate!(mixin(getStuff))
807             || isFunctionPointer!(mixin(getStuff)))
808         {
809             foreach (attribute; __traits(getAttributes, __traits(getMember, this, member)))
810             static if (is(attribute == SetGet)) 
811             {
812                 alias Type = typeof(__traits(getMember, this, member));
813                 auto propPtr = &__traits(getMember, this, member);
814                 static if (isFieldPrefix(member[0]))
815                 auto propName = member[1..$];
816                 else auto propName = member;
817                 auto descriptor = publication!Type(propName, true);
818                 descriptor.define(propPtr, propName);
819                 //
820                 static if (is(T : Object)) descriptor.declarator = cast(Object)this;
821                 static if (is(Type : Object))
822                 {
823                     auto o = *cast(Object*) propPtr;
824                     PropertyPublisher pub = cast(PropertyPublisher) o;
825                     // RAII: if it's initialized then it's mine
826                     if (pub) pub.declarator = this;
827                 }
828                 //
829                 enum h = getUDAs!(__traits(getMember, T, member), PropHints);
830                 static if (h.length) descriptor.hints = h[0];
831                 //
832                 version(none) writeln(attribute.stringof, " : ", member);
833                 break;
834             }
835         }
836     }
837     
838     /**
839      * Creates the property descriptors for the setter/getter pairs 
840      * annotated with @Set/@Get.
841      *
842      * In a class hierarchy, an overriden accessor replaces the ancestor's one.
843      * If a setter is annoted with @HideSet or a getter with @HideGet then
844      * the descriptor created when the ancestor was scanned is removed from the
845      * publications.
846      */
847     pragma(inline, false)
848     public void collectPublicationsFromPairs(T)()
849     {
850         import iz.types: ScopedReachability;
851         import iz.rtti: Rtti, getRtti;
852         import std.traits: Parameters, ReturnType, getSymbolsByUDA, getUDAs;
853         import std.meta: AliasSeq, staticIndexOf;
854         import std.algorithm.searching: countUntil;
855         import std.format: format;
856 
857         // collect
858         mixin ScopedReachability;
859         foreach (member; __traits(derivedMembers, T))
860         static if (isMemberReachable!(T, member))
861         foreach (overload; __traits(getOverloads, T, member))
862         {
863             alias Attributes = AliasSeq!(__traits(getAttributes, overload));
864             enum bool getterAttrib = staticIndexOf!(Get, Attributes) != -1;
865             enum bool setterAttrib = staticIndexOf!(Set, Attributes) != -1;
866             enum bool ungetAttrib  = staticIndexOf!(HideGet, Attributes) != -1;
867             enum bool unsetAttrib  = staticIndexOf!(HideSet, Attributes) != -1;
868             // define the getter
869             static if (getterAttrib && !ungetAttrib)
870             {
871                 alias Type = ReturnType!overload;
872                 const(Rtti)* ti = getRtti!Type;
873                 alias DescriptorType = PropDescriptor!Type;
874                 auto descriptor = publication!(Type)(member, true);
875                 auto dg = &overload;
876                 version(assert) if (descriptor.setter) assert (
877                     // note: rtti unqalifies the type
878                     ti is descriptor.rtti,
879                     "setter and getter type mismatch for " ~ descriptor.name[]);
880                 descriptor.define(descriptor.setter, dg, member);
881                 //
882                 static if (is(T : Object)) descriptor.declarator = cast(Object)this;
883                 static if (is(Type : Object))
884                 {
885                     auto o = cast(Object) dg();
886                     PropertyPublisher pub = cast(PropertyPublisher) o;
887                     // RAII: if it's initialized then it's mine
888                     if (pub) pub.declarator = this;
889                 }
890                 //
891                 enum h = getUDAs!(overload, PropHints);
892                 static if (h.length) descriptor.hints = h[0];
893                 //
894                 version(none) writeln(attribute.stringof, " < ", member);
895             }
896             // define the setter
897             else static if (setterAttrib && !unsetAttrib)
898             {
899                 alias Type = Parameters!overload;
900                 const(Rtti)* ti = getRtti!Type;
901                 version(assert) static assert(Type.length == 1,
902                     "setter must only have one parameter");
903                 alias DescriptorType = PropDescriptor!Type;
904                 auto descriptor = publication!(Parameters!overload)(member, true);
905                 auto dg = &overload;
906                 version(assert) if (descriptor.getter) assert (
907                     ti == descriptor.rtti,
908                     "setter and getter type mismatch for " ~ descriptor.name[]);
909                 descriptor.define(dg, descriptor.getter, member);
910                 //
911                 enum h = getUDAs!(overload, PropHints);
912                 static if (h.length) descriptor.hints = h[0];
913                 //
914                 version(none) writeln(attribute.stringof, " > ", member);
915             }
916             // hide from this descendant
917             else static if (ungetAttrib || unsetAttrib)
918             {
919                 if (auto descr = publication!size_t(member, false))
920                 {
921                     const ptrdiff_t index = _publishedDescriptors[].countUntil(descr);
922                     assert(index != -1);
923                     destruct(cast(GenericDescriptor*)_publishedDescriptors[index]);
924                     _publishedDescriptors = _publishedDescriptors[0..index] ~ _publishedDescriptors[index+1 .. $];
925                 }
926             }
927         }
928         // check
929         bool flag;
930         string[] names;
931         foreach (immutable i; 0 .. _publishedDescriptors.length)
932         {
933             GenericDescriptor* descr = publicationFromIndex(i);
934             if (descr.setter is null || descr.getter is null)
935             {
936                 flag = true;
937                 names ~= descr.name[];
938             }
939         }
940         if (flag)
941         {
942             enum messg =
943                 "Several invalid property descriptors detected.\n" ~
944                 "The setter or the getter is missing for %s.\n" ~
945                 "Tip: check for typos in the function names.";
946             throw new Error(format(messg, names));
947         }
948     }
949 }
950 
951 unittest
952 {
953     // test basic PropertyPublisher features: get descriptors, use them.
954     class Foo: PropertyPublisher
955     {
956         mixin PropertyPublisherImpl;
957         this(A...)(A a)
958         {
959             collectPublicationsFromFields!Foo;
960             collectPublicationsFromPairs!Foo;
961         }
962 
963         @SetGet private uint _anUint;
964         @SetGet private static char[] _manyChars;
965         private uint _a, _b;
966         private char[] _c;
967 
968         @PropHints(PropHint.initCare)
969         @Get uint propA(){return _a;}
970 
971         @Set void propA(uint aValue){_a = aValue;}
972 
973         @PropHints(PropHint.initCare,PropHint.dontSet)
974         @Get uint propB(){return _b;}
975         @Set void propB(uint aValue){_b = aValue;}
976 
977         @Get char[] propC(){return _c;}
978         @Set void propC(char[] aValue){_c = aValue;}
979 
980         void use()
981         {
982             assert(propA == 0);
983             auto aDescriptor = publication!uint("propA");
984             aDescriptor.setter()(1234_5678);
985             assert(propA == 1234_5678);
986 
987             assert(propB == 0);
988             auto bDescriptor = publication!uint("propB");
989             bDescriptor.setter()(8765_4321);
990             assert(propB == 8765_4321);
991 
992             assert(!propC.length);
993             auto cDescriptor = publication!(char[])("propC");
994             cDescriptor.setter()("Too Strange To Be Good".dup);
995             assert(propC == "Too Strange To Be Good");
996             propC = "Too Good To Be Strange".dup;
997             assert( publication!(char[])("propC").getter()() == "Too Good To Be Strange");
998 
999             assert(_anUint == 0);
1000             auto anUintDescriptor = publication!uint("anUint");
1001             anUintDescriptor.setter()(1234_5678);
1002             assert(_anUint == 1234_5678);
1003 
1004             assert(_manyChars == null);
1005             auto manyCharsDescriptor = publication!(char[])("manyChars");
1006             manyCharsDescriptor.setter()("BimBamBom".dup);
1007             assert(_manyChars == "BimBamBom");
1008             _manyChars = "BomBamBim".dup;
1009             assert(manyCharsDescriptor.getter()() == "BomBamBim");
1010         }
1011     }
1012     Foo foo = construct!Foo;
1013     foo.use;
1014     foo.destruct;
1015 }
1016 
1017 unittest
1018 {
1019     class Bar
1020     {
1021         size_t _field;
1022         string info;
1023         mixin PropertyPublisherImpl;
1024         this()
1025         {
1026             collectPublicationsFromPairs!Bar;
1027         }
1028         @Set void field(size_t aValue)
1029         {
1030             info ~= "Bar";
1031         }
1032         @Get size_t field()
1033         {
1034             info = "less derived";
1035             return _field;
1036         }
1037     }
1038     class Baz : Bar
1039     {
1040         @Set override void field(size_t aValue)
1041         {
1042             super.field(aValue);
1043             info ~= "Baz";
1044         }
1045         @Get override size_t field()
1046         {
1047             info = "most derived";
1048             return _field;
1049         }
1050     }
1051 
1052     // test that the most derived override is used as setter or getter
1053     Baz baz = construct!Baz;
1054     assert(baz.publicationCount == 1);
1055     auto prop = baz.publication!size_t("field");
1056     prop.set(0);
1057     assert(baz.info == "BarBaz");
1058     assert(baz.publicationCount == 1);
1059     auto a = prop.get;
1060     assert(baz.info == "most derived");
1061     baz.destruct;
1062 }
1063 
1064 unittest
1065 {
1066     alias Delegate = void delegate(uint a);
1067     class Cat
1068     {
1069         mixin PropertyPublisherImpl;
1070         @SetGet Delegate _meaow;
1071         this(){collectPublications!Cat;}
1072     }
1073 
1074     class Fly
1075     {
1076         mixin PropertyPublisherImpl;
1077         @GetSet string _bzzz(){return "bzzz";}
1078         this(){collectPublications!Fly;}
1079     }
1080 
1081     // test that a delegate is detected as a field
1082     Cat cat = construct!Cat;
1083     assert(cat.publicationCount == 1);
1084     auto descr = cast(PropDescriptor!uint*) cat.publicationFromIndex(0);
1085     assert(descr);
1086     assert(descr.rtti.type == RtType._funptr);
1087     // test that a plain function is not detected as field
1088     Fly fly = construct!Fly;
1089     assert(fly.publicationCount == 0);
1090     destructEach(cat, fly);
1091 }
1092 
1093 unittest
1094 {
1095     class Bee
1096     {
1097         mixin PropertyPublisherImpl;
1098         this(){collectPublicationsFromPairs!Bee;}
1099         @Set void delegate(uint) setter;
1100         @Get int delegate() getter;
1101     }
1102     // test that delegates as fields are not detected as set/get pairs
1103     Bee bee = construct!Bee;
1104     assert(bee.publicationCount == 0);
1105     destruct(bee);
1106 }
1107 
1108 unittest
1109 {
1110     class B0
1111     {
1112         mixin PropertyPublisherImpl;
1113         this(){collectPublications!B0;}
1114         @SetGet int _a;
1115     }
1116     class B1: B0
1117     {
1118         mixin PropertyPublisherImpl;
1119         this(){collectPublications!B1;}
1120         @Set void b(int value){}
1121         @Get int b(){return 0;}
1122         @SetGet int _c;
1123     }
1124     // test that all props are detected in the inheritence list
1125     auto b1 = construct!B1;
1126     assert(b1.publicationCount == 3);
1127     destruct(b1);
1128 }
1129 
1130 unittest
1131 {
1132     class B0
1133     {
1134         mixin PropertyPublisherImpl;
1135         this(){collectPublications!B0;}
1136         @Set void b(int value){}
1137         @Get int b(){return 0;}
1138     }
1139     class B1: B0
1140     {
1141         mixin PropertyPublisherImpl;
1142         this(){collectPublications!B1;}
1143         @HideGet override int b(){return super.b();}
1144     }
1145     // test that a prop marked with @HideSet/Get is not published anymore
1146     auto b0 = construct!B0;
1147     assert(b0.publicationCount == 1);
1148     auto b1 = construct!B1;
1149     assert(b1.publicationCount == 0);
1150     destructEach(b0,b1);
1151 }
1152 
1153 unittest
1154 {
1155     struct Bug
1156     {
1157         mixin PropertyPublisherImpl;
1158         this(uint value){collectPublications!Bug;}
1159         @SetGet uint _a;
1160     }
1161     // test that the 'static if things' related to 'interface inheritance'
1162     // dont interfere when mixed in struct
1163     Bug bug = Bug(0);
1164     assert(bug.publicationCount == 1);
1165     assert(bug.publicationFromName("a") != null);
1166     assert(bug.publicationFromName("a") == bug.publicationFromIndex(0));
1167 }
1168 
1169 unittest
1170 {
1171     // test safety, multiple setter types
1172     enum decl = q{
1173         class Bug
1174         {
1175             mixin PropertyPublisherImpl;
1176             this(){collectPublications!Bug;}
1177             @Set void b(int value, uint ouch){}
1178             @Get int b(){return 0;}
1179         }
1180     };
1181     static assert( !__traits(compiles, mixin(decl)));
1182 }
1183 
1184 version(checkleaks){} else unittest
1185 {
1186     // test safety, setter & getter types mismatch
1187     version(assert)
1188     {
1189         bool test;
1190         class Bug
1191         {
1192             mixin PropertyPublisherImpl;
1193             this(){collectPublications!Bug;}
1194             @Set void b(string value){}
1195             @Get int b(){return 0;}
1196         }
1197         Bug b;
1198         try b = construct!Bug;
1199         catch(Error e) test = true;
1200         assert(test);
1201         destruct(b);
1202     }
1203 }
1204 
1205 unittest
1206 {
1207     // test initial collector/declarator/ownership
1208     class B: PropertyPublisher
1209     {
1210         mixin PropertyPublisherImpl;
1211         this(){collectPublications!B;}
1212     }
1213     class A: PropertyPublisher
1214     {
1215         mixin PropertyPublisherImpl;
1216         B _owned2;
1217         this()
1218         {
1219             _owned1 = construct!B;
1220             _owned2 = construct!B;
1221             collectPublications!A;
1222         }
1223         ~this()
1224         {
1225             destructEach(_owned1, _owned2);
1226         }
1227         @SetGet uint _a;
1228         // ownership is set in getFields
1229         @SetGet B _owned1;
1230         // ownership is set in getPairs
1231         @Get B owned2(){return _owned2;}
1232         @Set void owned2(B value){}
1233         // ownership is not set because value initially is null
1234         @SetGet B _notowned;
1235     }
1236     auto a1 = construct!A;
1237     auto a2 = construct!A;
1238     a1._notowned = a2._owned1;
1239     //
1240     assert(a1._owned1.declarator is a1);
1241     assert(a1._owned2.declarator is a1);
1242     assert(a1._notowned.declarator is a2);
1243     destructEach(a1,a2);
1244 }
1245 
1246 version(checkleaks){} else unittest
1247 {
1248     // test that invalid pairs throw
1249     class Test: PropertyPublisher
1250     {
1251         mixin PropertyPublisherImpl;
1252         this(){collectPublications!Test;}
1253         // missing getter.
1254         @Set void error1(Object o){}
1255         // typo in funct name.
1256         @Get Object errror2(){return null;}
1257         @Set void error2(Object o){}
1258     }
1259     bool ouch;
1260     Test test;
1261     try test = construct!Test;
1262     catch(Error e)ouch = true;
1263     assert(ouch);
1264     destruct(test);
1265 }
1266 
1267 unittest
1268 {
1269     struct A{}
1270     class B
1271     {
1272         mixin PropertyPublisherImpl;
1273         A _a;
1274         @Get ref A a(){return _a;}
1275         @Set void a(A){}
1276         this()
1277         {
1278             collectPublications!B;
1279         }
1280     }
1281     B b = construct!B;
1282     destruct(b);
1283 }
1284 
1285 /**
1286  * Returns true if the target of a PropDescriptor!Object is owned by another
1287  * object.
1288  *
1289  * The serializer and the binders use this to determine if the members of a
1290  * nested object have to be copied / serialized or rather just the reference.
1291  *
1292  * Params:
1293  *      descriptor = A pointer to the target accessor.
1294  *      t = The potential owner.
1295  */
1296 bool targetObjectOwnedBy(T)(PropDescriptor!Object* descriptor, T t)
1297 if (isPropertyPublisher!T)
1298 {
1299     auto o = cast(PropertyPublisher) descriptor.get();
1300     if (o)
1301         return o.declarator !is t.declarator;
1302     else
1303         return false;
1304 }
1305 
1306 /**
1307  * Constrains the target of a PropDescriptor!Object to be owned
1308  * by a particular object.
1309  *
1310  * Params:
1311  *      descriptor = A pointer to the target accessor.
1312  *      t = The future owner.
1313  */
1314 void setTargetObjectOwner(T)(PropDescriptor!Object* descriptor, T t)
1315 if (isPropertyPublisher!T)
1316 {
1317     if (auto o = cast(PropertyPublisher) descriptor.get())
1318     {
1319         o.declarator = t;
1320         descriptor.declarator = t;
1321     }
1322 }
1323 
1324 unittest
1325 {
1326     class Foo(bool Nested) : PropertyPublisher
1327     {
1328         mixin PropertyPublisherImpl;
1329         this()
1330         {
1331             static if (Nested)
1332                 _full = construct!(Foo!false);
1333             collectPublications!Foo;
1334         }
1335 
1336         ~this()
1337         {
1338             static if (Nested)
1339                 destruct(_full);
1340         }
1341 
1342         @SetGet Foo!false _asref;
1343         static if (Nested)
1344         @SetGet Foo!false _full;
1345     }
1346     auto foo = construct!(Foo!true);
1347     assert(targetObjectOwnedBy(foo.publication!Object("full"), foo));
1348     assert(!targetObjectOwnedBy(foo.publication!Object("asref"), foo));
1349     destruct(foo);
1350 }
1351 
1352 /**
1353  * Destructs the PropertyPublishers that are published and owned by an object.
1354  *
1355  * Note that after the call, the references to the objects that are destructed
1356  * are dangling.
1357  *
1358  * Params:
1359  *      recursive = If set to true, the default, the function is called on the
1360  *      sub-publishers before being destructed.
1361  *      pub = The PropertyPublisher whose sub-publishers have to be destructed.
1362  */
1363 void destructOwnedPublishers(bool recursive = true)(PropertyPublisher pub)
1364 {
1365     foreach(immutable i; 0 .. pub.publicationCount)
1366     {
1367         PropDescriptor!Object* d = cast(PropDescriptor!Object*)
1368             pub.publicationFromIndex(i);
1369 
1370         if (!d || d.rtti.type != RtType._object || !d.declarator)
1371             continue;
1372 
1373         Object obj = d.get();
1374         if (!obj)
1375             continue;
1376 
1377         PropertyPublisher sub =  cast(PropertyPublisher) obj;
1378         if (!sub || !sub.declarator)
1379             continue;
1380 
1381         if (sub.declarator !is d.declarator)
1382             continue;
1383 
1384         static if (recursive)
1385             destructOwnedPublishers(sub);
1386 
1387         destruct(obj);
1388     }
1389 }
1390 ///
1391 unittest
1392 {
1393     class A19: PropertyPublisher
1394     {mixin PropertyPublisherImpl; string c;}
1395 
1396     class B19: PropertyPublisher
1397     {
1398         mixin PropertyPublisherImpl;
1399         @SetGet A19 _a0;
1400         @SetGet A19 _a1;
1401         this()
1402         {
1403             _a0 = construct!A19;
1404             collectPublications!B19;
1405             // _a1 not seen by the scanner so not owned
1406             _a1 = construct!A19;
1407         }
1408         ~this()
1409         {
1410             destruct(_a1);
1411         }
1412     }
1413     B19 b = construct!B19;
1414     // implicitly destructs b._a0.
1415     destructOwnedPublishers(b);
1416     destruct(b);
1417 }
1418 
1419 unittest
1420 {
1421     class A: PropertyPublisher {mixin PropertyPublisherImpl;}
1422     A a = construct!A;
1423     destruct(a);
1424 }
1425 
1426 //TODO-cproperties: findPublisher overload that can also take a pub struct and return a pointer to a publisher
1427 
1428 /**
1429  * Finds a sub-publisher in an object's publications tree.
1430  *
1431  * Params:
1432  *      pub = The root publisher, either a class or a struct.
1433  *      accessChain = A string made of property names separated by dots.
1434  *      It must not starts with the name of the root.
1435  * Returns:
1436  *      null if the sub-publisher could not be found.
1437  *      null if the sub-publisher could be found but doesn't exist yet.
1438  *      The matching publisher otherwise.
1439  */
1440 PropertyPublisher findPublisher(PropertyPublisher pub, string accessChain)
1441 {
1442     import iz.strings: bySeparated;
1443     auto names = accessChain.bySeparated('.');
1444 
1445     while(true)
1446     {
1447         if (!pub || names.empty)
1448             return pub;
1449         if (auto descr = cast(PropDescriptor!Object*) pub.publicationFromName(names.front))
1450         {
1451             pub = null;
1452             if (descr.rtti.type == RtType._object)
1453                 if (Object o = descr.get())
1454                     pub = cast(PropertyPublisher) o;
1455         }
1456         else pub = null;
1457         names.popFront;
1458     }
1459 }
1460 ///
1461 unittest
1462 {
1463     class AU: PropertyPublisher
1464     {mixin PropertyPublisherImpl;}
1465 
1466     class B: PropertyPublisher
1467     {
1468         mixin PropertyPublisherImpl;
1469         mixin inheritedDtor;
1470 
1471         @SetGet AU _a;
1472         this()
1473         {
1474             _a = construct!AU;
1475             collectPublications!B;
1476         }
1477         ~this()
1478         {
1479             destruct(_a);
1480         }
1481     }
1482 
1483     class C: PropertyPublisher
1484     {
1485         mixin PropertyPublisherImpl;
1486         mixin inheritedDtor;
1487 
1488         @SetGet B _b;
1489         this()
1490         {
1491             _b = construct!B;
1492             collectPublications!C;
1493         }
1494         ~this()
1495         {
1496             destruct(_b);
1497         }
1498     }
1499 
1500     C c = construct!C;
1501 
1502     assert(c.findPublisher("b") == c._b);
1503     assert(c.findPublisher("b.a") == c._b._a);
1504     assert(c.findPublisher("b.a.g") is null);
1505     destruct(c);
1506 }
1507 
1508 /**
1509  * Returns true if two property descriptors are bindable.
1510  * To be bindable, the property name must be identical but also their types.
1511  * Type checking is based on RTTI and neither source nor target
1512  * needs to be passed with the correct type.
1513  */
1514 bool areBindable(S,T)(PropDescriptor!S* source, PropDescriptor!T* target)
1515 in
1516 {
1517     assert(source);
1518     assert(target);
1519     assert(cast(void*)target != cast(void*)source);
1520 }
1521 body
1522 {
1523     return source.name == target.name && source.rtti is target.rtti;
1524 }
1525 ///
1526 unittest
1527 {
1528     int a,b;
1529 
1530     // exact match
1531     PropDescriptor!int aDescriptor = PropDescriptor!int(&a, "a");
1532     PropDescriptor!int bDescriptor = PropDescriptor!int(&b, "a");
1533     assert(areBindable(&aDescriptor, &bDescriptor));
1534 
1535     // it also works with different static types thanks to the RTTI
1536     auto cDescriptor = cast(PropDescriptor!float*) &aDescriptor;
1537     assert(areBindable(cDescriptor, &bDescriptor));
1538 }
1539 
1540 /**
1541  * Binds two property publishers.
1542  *
1543  * After the call, each property published in source and target that have the
1544  * same runtime type and the same name share the same value.
1545  *
1546  * Params:
1547  *      recursive = Indicates if the process is recursive.
1548  *      src = The aggregate from where the properties values are copied.
1549  *          Either a class or a struct that's mixed with PropertyPublisherImpl
1550  *          or a PropertyPublisher.
1551  *      trg = The aggregate to where the properties values are copied.
1552  *          The Target type has the same requirement as the source type.
1553  */
1554 void bindPublications(bool recursive = false, S, T)(auto ref S src, auto ref T trg)
1555 {
1556     // try to get a publisher from src
1557     static if (is(S == class))
1558     {
1559         PropertyPublisher source = cast(PropertyPublisher) src;
1560         if (!src)
1561             return;
1562     }
1563     else static if (is(S == PropertyPublisher))
1564     {
1565         alias source = src;
1566         if (!src)
1567             return;
1568     }
1569     else static if (is(S == const(PubTraits)))
1570     {
1571         alias source = src;
1572     }
1573     else static if (is(S == struct))
1574     {
1575         if (!isPublisingStruct(getRtti!S))
1576             return;
1577         alias source = src;
1578     }
1579     else static assert(0, S.stringof ~ " is not a property publisher");
1580 
1581     // try to get a publisher from trg
1582     static if (is(T == class))
1583     {
1584         PropertyPublisher target = cast(PropertyPublisher) trg;
1585         if (!trg)
1586             return;
1587     }
1588     else static if (is(T == PropertyPublisher))
1589     {
1590         alias target = trg;
1591         if (!trg)
1592             return;
1593     }
1594     else static if (is(T == const(PubTraits)))
1595     {
1596         alias target = trg;
1597     }
1598     else static if (is(T == struct))
1599     {
1600         if (!isPublisingStruct(getRtti!T))
1601             return;
1602         alias target = trg;
1603     }
1604     else static assert(0, T.stringof ~ " cannot be a PropertyPublisher");
1605 
1606     enum PubIsStruct = is(S == struct);
1607 
1608     // bind publications
1609     GenericDescriptor* srcP, trgP;
1610     foreach(immutable i; 0 .. source.publicationCount())
1611     {
1612         srcP = cast(GenericDescriptor*) source.publicationFromIndex(i);
1613         trgP = cast(GenericDescriptor*) target.publicationFromName(srcP.name[]);
1614 
1615         if (!trgP) continue;
1616         if (srcP.rtti !is trgP.rtti) continue;
1617 
1618         void setBasicType(T)()
1619         {
1620             final switch (srcP.rtti.dimension != 0)
1621             {
1622                 // value ABI
1623                 case false:
1624                     alias DT0 = PropDescriptor!T*;
1625                     (cast(DT0) trgP).set((cast(DT0) srcP).get());
1626                     break;
1627                 // array ABI
1628                 case true:
1629                     alias DT1 = PropDescriptor!(T[])*;
1630                     (cast(DT1) trgP).set((cast(DT1) srcP).get());
1631                     break;
1632             }
1633         }
1634 
1635         void setAA()
1636         {
1637             alias DT = PropDescriptor!(int[int])*;
1638             (cast(DT) trgP).set((cast(DT) srcP).get());
1639         }
1640 
1641         void setObject()
1642         {
1643             static if (!PubIsStruct) // PubTraits should also have declarator, even if not used
1644             {
1645                 const Object srcObj = srcP.as!Object.get();
1646                 const Object trgObj = trgP.as!Object.get();
1647                 if (!srcObj || !trgObj)
1648                     return;
1649 
1650                 PropertyPublisher sPb = cast(PropertyPublisher) srcObj;
1651                 PropertyPublisher tPb = cast(PropertyPublisher) trgObj;
1652                 if (!sPb || !tPb)
1653                     return;
1654 
1655                 // reference
1656                 if (sPb.declarator !is cast(Object) source
1657                     && tPb.declarator !is cast(Object) target)
1658                         setBasicType!Object;
1659                 // sub object
1660                 else static if (recursive)
1661                 {
1662                     bindPublications!true(sPb, tPb);
1663                 }
1664             }
1665         }
1666 
1667         void setStruct()
1668         {
1669             auto srcPd = cast(PropDescriptor!GenericStruct*) srcP;
1670             auto trgPd = cast(PropDescriptor!GenericStruct*) trgP;
1671             void* srcStruct = getThis(srcPd.getter()());
1672             void* trgStruct = getThis(trgPd.getter()());
1673             const(StructInfo)* srcStructRtti(){return srcP.rtti.structInfo;}
1674             const(StructInfo)* trgStructRtti(){return trgP.rtti.structInfo;}
1675             with(StructType) final switch(srcStructRtti.type)
1676             {
1677                 case _none:
1678                     break;
1679                 case _text:
1680                     void* oldPtr = srcStructRtti.textTraits.setContext(srcStruct);
1681                     const(char)[] value = srcStructRtti.textTraits.saveToText();
1682                     srcStructRtti.textTraits.restoreContext(oldPtr);
1683                     oldPtr = trgStructRtti.textTraits.setContext(trgStruct);
1684                     trgStructRtti.textTraits.loadFromText(value);
1685                     trgStructRtti.textTraits.restoreContext(oldPtr);
1686                     break;
1687                 case _binary:
1688                     void* oldPtr = srcStructRtti.binTraits.setContext(srcStruct);
1689                     ubyte[] value = srcStructRtti.binTraits.saveToBytes();
1690                     srcStructRtti.binTraits.restoreContext(oldPtr);
1691                     oldPtr = trgStructRtti.binTraits.setContext(trgStruct);
1692                     trgStructRtti.binTraits.loadFromBytes(value);
1693                     trgStructRtti.binTraits.restoreContext(oldPtr);
1694                     break;
1695                 case _publisher:
1696                     static if (recursive)
1697                     {
1698                         const(PubTraits) srcPub = *srcStructRtti.pubTraits;
1699                         const(PubTraits) trgPub = *trgStructRtti.pubTraits;
1700                         srcPub.setContext(srcStruct);
1701                         trgPub.setContext(trgStruct);
1702                         bindPublications!true(srcPub, trgPub);
1703                     }
1704             }
1705         }
1706 
1707         import iz.streams: Stream;
1708 
1709         with(RtType) final switch (srcP.rtti.type)
1710         {
1711             case _invalid: case _union: break;
1712             case _aa:       setAA; break;
1713             case _bool:     setBasicType!bool; break;
1714             case _ubyte:    setBasicType!ubyte; break;
1715             case _byte:     setBasicType!byte; break;
1716             case _ushort:   setBasicType!ushort; break;
1717             case _short:    setBasicType!short; break;
1718             case _uint:     setBasicType!uint; break;
1719             case _int:      setBasicType!int; break;
1720             case _ulong:    setBasicType!ulong; break;
1721             case _long:     setBasicType!long; break;
1722             case _float:    setBasicType!float; break;
1723             case _double:   setBasicType!double; break;
1724             case _real:     setBasicType!real; break;
1725             case _char:     setBasicType!char; break;
1726             case _wchar:    setBasicType!wchar; break;
1727             case _dchar:    setBasicType!dchar; break;
1728             case _stream:   setBasicType!Stream; break;
1729             case _funptr:   setBasicType!GenericFunction; break;
1730             case _enum:     setBasicType!int; break;
1731             case _struct:   setStruct; break;
1732             case _object:   setObject; break;
1733             case _pointer:  setBasicType!(int*);
1734         }
1735     }
1736 }
1737 
1738 unittest
1739 {
1740     import iz.streams: Stream, MemoryStream;
1741 
1742     static struct T23Bytes
1743     {
1744         enum ubyte[] _value = [0,1,2,3];
1745         ubyte[] saveToBytes() {return _value;}
1746         void loadFromBytes(ubyte[] value){assert(value == _value);}
1747     }
1748 
1749     static struct T23Text
1750     {
1751         enum const(char)[] _value = "value";
1752         const(char)[] saveToText() {return _value;}
1753         void loadFromText(const(char)[] value){assert(value == _value);}
1754     }
1755 
1756     class T23Foo(bool Nested) : PropertyPublisher
1757     {
1758         mixin PropertyPublisherImpl;
1759         mixin inheritedDtor;
1760 
1761         this()
1762         {
1763             static if (Nested)
1764                 _sub = construct!(T23Foo!false);
1765             str = construct!MemoryStream;
1766             collectPublications!(T23Foo!Nested);
1767 
1768             static if (Nested)
1769                 assert(_sub.declarator is this);
1770 
1771         }
1772         ~this()
1773         {
1774             // TODO-cbugfix: nested prop publisher bug after binding
1775             static if (Nested) if (_sub)
1776                 destruct(_sub);
1777             destruct(str);
1778         }
1779         @SetGet uint _a;
1780         @SetGet ulong _b;
1781         @SetGet string _c;
1782         @SetGet int[][] _d;
1783         @SetGet int[][][] _e;
1784         @SetGet T23Bytes _bytes;
1785         @SetGet T23Text _text;
1786         @SetGet int[string] _f;
1787         MemoryStream str;
1788 
1789         enum Ea{a0, a1}
1790         @SetGet Ea _ea;
1791 
1792         @Set void stream(Stream s)
1793         {str.loadFromStream(s);}
1794         @Get Stream stream(){return str;}
1795 
1796         static if (Nested)
1797         {
1798             @NoGc @SetGet T23Foo!false _sub;
1799         }
1800     }
1801 
1802     T23Foo!true source = construct!(T23Foo!true);
1803     T23Foo!true target = construct!(T23Foo!true);
1804     source._a = 8; source._b = ulong.max; source._c = "123";
1805     source._d = [[0,1,2,3],[4,5,6,7]];
1806     source._e = [[[0,1],[2,3]],[[4,5],[6,7]],[[8,9],[10,11]]];
1807     source._f = ["a":0, "b":1];
1808     source._ea = source.Ea.a1;
1809     source._sub._a = 8; source._sub._b = ulong.max; source._sub._c = "123";
1810     source.str.writeInt(1);source.str.writeInt(2);source.str.writeInt(3);
1811     source._sub.str.writeInt(1);source._sub.str.writeInt(2);source._sub.str.writeInt(3);
1812 
1813     bindPublications!true(source, target);
1814 
1815     assert(target._a == source._a);
1816     assert(target._b == source._b);
1817     assert(target._c == source._c);
1818     assert(target._d == source._d);
1819     assert(target._e == source._e);
1820     assert(target._f == source._f);
1821     assert(target._ea == source.Ea.a1);
1822     assert(target._sub._a == source._sub._a);
1823     assert(target._sub._b == source._sub._b);
1824     assert(target._sub._c == source._sub._c);
1825     target.str.position = 0;
1826     assert(target.str.readInt == 1);
1827     assert(target.str.readInt == 2);
1828     assert(target.str.readInt == 3);
1829     target._sub.str.position = 0;
1830     assert(target._sub.str.readInt == 1);
1831     assert(target._sub.str.readInt == 2);
1832     assert(target._sub.str.readInt == 3);
1833 
1834     destructEach(source, target);
1835 }
1836 
1837 unittest
1838 {
1839     static struct Child
1840     {
1841         mixin PropertyPublisherImpl;
1842         @SetGet int _a = 8;
1843         @SetGet int _b = 9;
1844         ~this() {this.clearDescriptors; }
1845     }
1846 
1847     static struct Parent
1848     {
1849         mixin PropertyPublisherImpl;
1850         @SetGet Child _child1;
1851         @SetGet Child _child2;
1852         ~this() {this.clearDescriptors; }
1853     }
1854 
1855     Parent p0, p1;
1856     p0.collectPublications!Parent;
1857     p0._child1.collectPublications!Child;
1858     p0._child2.collectPublications!Child;
1859     p1.collectPublications!Parent;
1860     p1._child1.collectPublications!Child;
1861     p1._child2.collectPublications!Child;
1862 
1863     assert(p0.publicationCount == 2);
1864     assert(p0._child1.publicationCount == 2);
1865     //
1866     p0._child1._a = 0;
1867     p0._child1._b = 0;
1868     p0._child2._a = 0;
1869     p0._child2._b = 0;
1870     //
1871     bindPublications!true(p0, p1);
1872     assert(p1._child1._a == 0);
1873     assert(p1._child1._b == 0);
1874     assert(p1._child2._a == 0);
1875     assert(p1._child2._b == 0);
1876 }
1877 
1878 /**
1879  * A PropertyBinder synchronizes the value of several variables between themselves.
1880  *
1881  * The access to the variables is done via the PropDescriptor format, hence
1882  * a PropertyBinder stores a list of PropDescriptor with the same types.
1883  *
1884  * Params:
1885  *      T = The type of the properties.
1886  *      RttiCheck = When set to true, an additional run-time check is performed.
1887  */
1888 class PropertyBinder(T, bool RttiCheck = false)
1889 {
1890 
1891     mixin inheritedDtor;
1892 
1893 private:
1894 
1895     ContiguousList!(PropDescriptor!T*) _itemsToDestruct;
1896     ContiguousList!(PropDescriptor!T*) _items;
1897     PropDescriptor!T *_source;
1898 
1899 public:
1900 
1901     ///
1902     this()
1903     {
1904         _items = construct!(ContiguousList!(PropDescriptor!T*));
1905         _itemsToDestruct = construct!(ContiguousList!(PropDescriptor!T*));
1906     }
1907 
1908     ~this()
1909     {
1910         foreach(immutable i; 0 .. _itemsToDestruct.count)
1911         {
1912             auto descr = _itemsToDestruct[i];
1913             if (descr) destruct(descr);
1914         }
1915         _items.destruct;
1916         _itemsToDestruct.destruct;
1917         callInheritedDtor;
1918     }
1919 
1920     /**
1921      * Adds a property to the list.
1922      * If the binder is not local then aProp should neither be a local descriptor,
1923      * otherwise the descritpor reference will become invalid.
1924      *
1925      * Params:
1926      *      aProp = A PropDescriptor of type T.
1927      *      isSource = Optional boolean indicating if the descriptor is used as
1928      *          master property.
1929      *
1930      * Returns:
1931      *      The index of the descriptor in the binding list.
1932      */
1933     ptrdiff_t addBinding(ref PropDescriptor!T prop, bool isSource = false)
1934     {
1935         static if (RttiCheck)
1936         {
1937             if (getRtti!T !is aProp.rtti)
1938                 return -1;
1939         }
1940         if (isSource) _source = &prop;
1941         return _items.add(&prop);
1942     }
1943 
1944     /**
1945      * Adds a new property to the list.
1946      * The life-time of the new descriptor is handled internally.
1947      *
1948      * Returns:
1949      *      A new PropDescriptor of type T.
1950      */
1951     PropDescriptor!T* newBinding()
1952     {
1953         auto result = construct!(PropDescriptor!T);
1954         _items.add(result);
1955         _itemsToDestruct.add(result);
1956         return result;
1957     }
1958 
1959     /**
1960      * Removes the aIndex-nth property from the list.
1961      * The item is freed if it has been allocated by newBinding.
1962      * source might be invalidated if it matches the item.
1963      *
1964      * Params:
1965      *      index = The index of the descriptor to remove.
1966      */
1967     void removeBinding(size_t index)
1968     {
1969         auto itm = _items.extract(index);
1970         if (_source && itm == _source)
1971             _source = null;
1972         if (_itemsToDestruct.remove(itm))
1973             destruct(itm);
1974     }
1975 
1976     /**
1977      * Triggers the setter of each property.
1978      * This method is usually called at the end of a setter method
1979      * (in the master/source setter).
1980      *
1981      * Params:
1982      *      value = the new value to send to binding.
1983      */
1984     void change(T value)
1985     {
1986         foreach(item; _items)
1987             if (item.setter)
1988                 item.set(value);
1989     }
1990 
1991     /**
1992      * Calls change() using the value of source.
1993      */
1994     void updateFromSource()
1995     {
1996         if (_source)
1997             change(_source.get());
1998     }
1999 
2000     /**
2001      * Sets the property used as source in updateFromSource().
2002      * Params:
2003      *      src = The property to be used as source.
2004      */
2005     void source(ref PropDescriptor!T src) {_source = &src;}
2006 
2007     /**
2008      * Returns the property used as source in _updateFromSource().
2009      */
2010     PropDescriptor!T* source() {return _source;}
2011 
2012     /**
2013      * Provides an access to the property descriptors for additional List operations.
2014      * Note that the items whose life-time is managed should not be modified.
2015      */
2016     ContiguousList!(PropDescriptor!T*) items() {return _items;}
2017 }
2018 
2019 unittest
2020 {
2021     alias intprops = PropertyBinder!int;
2022     alias floatprops = PropertyBinder!float;
2023 
2024     class Foo
2025     {
2026         private
2027         {
2028             int fA;
2029             float fB;
2030             intprops fASlaves;
2031             floatprops fBSlaves;
2032         }
2033         public
2034         {
2035             this()
2036             {
2037                 fASlaves = construct!intprops;
2038                 fBSlaves = construct!floatprops;
2039             }
2040             ~this()
2041             {
2042                 fASlaves.destruct;
2043                 fBSlaves.destruct;
2044             }
2045             void A(int value)
2046             {
2047                 if (fA == value) return;
2048                 fA = value;
2049                 fASlaves.change(fA);
2050             }
2051             int A(){return fA;}
2052 
2053             void B(float value)
2054             {
2055                 if (fB == value) return;
2056                 fB = value;
2057                 fBSlaves.change(fB);
2058             }
2059             float B(){return fB;}
2060 
2061             void addABinding(ref intProp aProp)
2062             {
2063                 fASlaves.addBinding(aProp);
2064             }
2065 
2066             void addBBinding(ref floatProp aProp)
2067             {
2068                 fBSlaves.addBinding(aProp);
2069             }
2070         }
2071     }
2072 
2073     class FooSync
2074     {
2075         private
2076         {
2077             int fA;
2078             float fB;
2079         }
2080         public
2081         {
2082             void A(int value){fA = value;}
2083             int A(){return fA;}
2084             void B(float value){fB = value;}
2085             float B(){return fB;}
2086         }
2087     }
2088 
2089     class Bar
2090     {
2091         public int A;
2092         public float B;
2093     }
2094 
2095     // 1 master, 2 slaves
2096     auto a0 = construct!Foo;
2097     auto a1 = construct!FooSync;
2098     auto a2 = construct!FooSync;
2099     auto a3 = construct!Bar;
2100 
2101     auto prp1 = intProp(&a1.A,&a1.A);
2102     a0.addABinding(prp1);
2103 
2104     auto prp2 = intProp(&a2.A,&a2.A);
2105     a0.addABinding(prp2);
2106 
2107     intProp prp3 = intProp(&a3.A);
2108     a0.addABinding(prp3);
2109 
2110     auto prpf1 = floatProp(&a1.B,&a1.B);
2111     auto prpf2 = floatProp(&a2.B,&a2.B);
2112     auto prpf3 = floatProp(&a3.B);
2113     a0.addBBinding(prpf1);
2114     a0.addBBinding(prpf2);
2115     a0.addBBinding(prpf3);
2116 
2117     a0.A = 2;
2118     assert( a1.A == a0.A);
2119     a1.A = 3;
2120     assert( a1.A != a0.A);
2121     a0.A = 4;
2122     assert( a2.A == a0.A);
2123     a0.A = 5;
2124     assert( a3.A == a0.A);
2125 
2126     a0.B = 2.5;
2127     assert( a1.B == a0.B);
2128     a1.B = 3.5;
2129     assert( a1.B != a0.B);
2130     a0.B = 4.5;
2131     assert( a2.B == a0.B);
2132     a0.B = 5.5;
2133     assert( a3.B == a0.B);
2134 
2135     // interdependent bindings
2136     auto m0 = construct!Foo;
2137     auto m1 = construct!Foo;
2138     auto m2 = construct!Foo;
2139 
2140     intProp mprp0 = intProp(&m0.A, &m0.A);
2141     intProp mprp1 = intProp(&m1.A, &m1.A);
2142     intProp mprp2 = intProp(&m2.A, &m2.A);
2143 
2144     m0.addABinding(mprp1);
2145     m0.addABinding(mprp2);
2146 
2147     m1.addABinding(mprp0);
2148     m1.addABinding(mprp2);
2149 
2150     m2.addABinding(mprp0);
2151     m2.addABinding(mprp1);
2152 
2153     m0.A = 2;
2154     assert( m1.A == m0.A);
2155     assert( m2.A == m0.A);
2156     m1.A = 3;
2157     assert( m0.A == m1.A);
2158     assert( m2.A == m1.A);
2159     m2.A = 4;
2160     assert( m1.A == m2.A);
2161     assert( m0.A == m2.A);
2162 
2163     a0.destruct;
2164     a1.destruct;
2165     a2.destruct;
2166     a3.destruct;
2167     m0.destruct;
2168     m1.destruct;
2169     m2.destruct;
2170 }
2171 
2172 unittest
2173 {
2174     auto strSync = construct!(PropertyBinder!int);
2175 
2176     class A
2177     {
2178         private int fStr;
2179         public void str(int aValue){fStr = aValue;}
2180         public int str(){return fStr;}
2181     }
2182 
2183     auto a0 = construct!A;
2184     auto a1 = construct!A;
2185     auto a2 = construct!A;
2186 
2187     auto propa0str = strSync.newBinding;
2188     propa0str.define(&a0.str,&a0.str);
2189     auto propa1str = strSync.newBinding;
2190     propa1str.define(&a1.str,&a1.str);
2191     auto propa2str = strSync.newBinding;
2192     propa2str.define(&a2.str,&a2.str);
2193 
2194     strSync.change(8);
2195 
2196     assert(a0.str == 8);
2197     assert(a1.str == 8);
2198     assert(a2.str == 8);
2199 
2200     a0.destruct;
2201     a1.destruct;
2202     a2.destruct;
2203     strSync.destruct;
2204 }
2205