www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Extensible struct type via variadic template arguments

reply Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
Hello all,

I've been playing around recently with some new ideas for a 
reworking of my Dgraph library:
https://github.com/WebDrake/Dgraph

... and particularly, considering how to use D's metaprogramming 
techniques to best allow the fundamental graph data structures to 
be extended arbitrarily.

One very basic idea is to allow graph edges to have arbitrary 
properties, and to do that, I've tried to come up with a basic 
data structure that can be extended via variadic template 
arguments.  However, I'm not entirely satisfied with the design, 
as I suspect it can be simplified/made nicer.  Here's what I have 
at present:


//////////////////////////////////////////

/**
  * Basic data structure containing arrays all of the same
  * length, such that fieldname[i] is the corresponding
  * property of edge i
  */
struct ExtensibleEdgeList (EdgePropertyList...)
{
     public size_t[] tail;
     public size_t[] head;

     mixin(multipleEdgeProperties!(EdgePropertyList));
}

/**
  * Custom edge properties can be specified in terms of
  * the type to be used and the name to be used
  */
struct EdgeProperty
{
     string type;
     string name;
}

/**
  * Template that evaluates to the string of code
  * describing the custom edge property fields
  */
template multipleEdgeProperties (EdgePropertyList...)
{
     static if (EdgePropertyList.length == 0)
     {
         const multipleEdgeProperties = ``;
     }
     else static if (EdgePropertyList.length == 1)
     {
         const multipleEdgeProperties = 
singleEdgeProperty!(EdgePropertyList[0]);
     }
     else
     {
         const multipleEdgeProperties = 
singleEdgeProperty!(EdgePropertyList[0]) ~ `\n` ~ 
multipleEdgeProperties!(EdgePropertyList[1 .. $]);
     }
}

/**
  * Template that evaluates to the string of code
  * for a single edge property field
  */
template singleEdgeProperty (EdgeProperty property)
{
     const singleEdgeProperty = `public ` ~ property.type ~ `[] ` 
~ property.name ~ `;`;
}

//////////////////////////////////////////


There are two things I'm wondering about the above design:

   * is it possible to redesign EdgeProperty as a template that 
would take
     a type parameter directly rather than as a string, so one 
could write
     e.g. EdgeProperty!("weight", double) ... ?

   * is it possible to rework things to avoid the string mixins 
...?

My metaprogramming-fu is a bit rusty these days (one reason for 
pursuing this project is to try and start exercising it 
again...), so I'd be very grateful for any thoughts or advice 
anyone could offer (on the above questions or the design in 
general).

Thanks & best wishes,

      -- Joe
Apr 10 2016
parent reply ag0aep6g <anonymous example.com> writes:
On 10.04.2016 20:45, Joseph Rushton Wakeling wrote:
 One very basic idea is to allow graph edges to have arbitrary
 properties, and to do that, I've tried to come up with a basic data
 structure that can be extended via variadic template arguments.
 However, I'm not entirely satisfied with the design, as I suspect it can
 be simplified/made nicer.  Here's what I have at present:


 //////////////////////////////////////////
[...]
 //////////////////////////////////////////


 There are two things I'm wondering about the above design:

    * is it possible to redesign EdgeProperty as a template that would take
      a type parameter directly rather than as a string, so one could write
      e.g. EdgeProperty!("weight", double) ... ?

    * is it possible to rework things to avoid the string mixins ...?
---- struct ExtensibleEdgeList (EdgePropertyList...) { public size_t[] tail; public size_t[] head; mixin multipleEdgeProperties!(EdgePropertyList); } template EdgeProperty(Type_, string name_) { alias Type = Type_; enum string name = name_; } mixin template multipleEdgeProperties (EdgePropertyList...) { static if (EdgePropertyList.length >= 1) { mixin singleEdgeProperty!(EdgePropertyList[0]); mixin multipleEdgeProperties!(EdgePropertyList[1 .. $]); } } mixin template singleEdgeProperty (alias property) { mixin(`public property.Type[] ` ~ property.name ~ `;`); } ---- The string mixin is hidden away in singleEdgeProperty, but it's still there. To get rid of it, you'd have take a different approach, I think. For example: ---- struct Foo { mixin EdgeListHeadTail; EdgeProperty!double weight; EdgeProperty!int bar; } mixin template EdgeListHeadTail () { public size_t[] tail; public size_t[] head; } alias EdgeProperty (Type) = Type[]; ---- No code generation anymore. But now the user must not forget the mixin of EdgeListHeadTail. And if you need to generate something from the number of edge properties, then this way isn't feasible at all, of course.
Apr 11 2016
parent Joseph Rushton Wakeling <joseph.wakeling webdrake.net> writes:
On Monday, 11 April 2016 at 22:06:00 UTC, ag0aep6g wrote:
 mixin template multipleEdgeProperties (EdgePropertyList...)
 {
     static if (EdgePropertyList.length >= 1)
     {
         mixin singleEdgeProperty!(EdgePropertyList[0]);
         mixin multipleEdgeProperties!(EdgePropertyList[1 .. $]);
     }
 }

 mixin template singleEdgeProperty (alias property)
 {
     mixin(`public property.Type[] ` ~ property.name ~ `;`);
 }
Ah, nice! I knew I was missing a trick somewhere with mixin templates, but I haven't used them for sufficiently long that my brain wasn't in the right place to find it. Your solution is _much_ more elegant than what I have now.
 The string mixin is hidden away in singleEdgeProperty, but it's 
 still there.

 To get rid of it, you'd have take a different approach, I think.
Yea, makes sense. I'm probably going to hang onto the broader details of the approach (a string mixin or two isn't _that_ horrendous...) because there are some other aspects to it that I would like to play with. But your suggested alternative is interesting to consider. Thanks very much for the useful ideas! :-)
Apr 11 2016