Implementing serialisation with minmal boilerplate and template overhead using core.reflect

drug drug2004 at bk.ru
Sun Aug 15 12:27:44 UTC 2021


15.08.2021 14:18, Stefan Koch пишет:
> Good Day everyone,
> 
> A friend asked me recently of core.reflect could be used for serialization.
> 
> The code to do that generically is about 100 lines of code.
> Since it's so little I am just going to post it here verbatim.
> 
> ```D
> class C { @("NoSerialize") ubyte a; uint b; @("NoSerialize") ulong c;
>      this(ubyte a, uint b, ulong c)
>      {
>          this.a = a; this.b = b; this.c = c;
>      }
> }
> struct S { @("NoSerialize") ubyte a; uint b; ubyte c; @("NoSerialize") 
> ulong d; }
> 
> import core.reflect.reflect;
> /// if a type is an aggregate type return the aggregate declaration
> /// otherwise return null
> AggregateDeclaration aggregateFromType(const Type type)
> {
>      AggregateDeclaration result = null;
>      if (auto ts = cast(TypeStruct)type)
>      {
>          result = ts.sym;
>      } else if (auto tc = cast(TypeClass)type)
>      {
>          result = tc.sym;
>      }
>      return result;
> }
> 
> bool hasNoSerializeAttrib(const Declaration d)
> {
>      bool NoSerialize = false;
>      foreach(attr;d.attributes)
>      {
>          auto se = cast(StringLiteral)attr;
>          if (se && se.string_ == "NoSerialize")
>          {
>            NoSerialize = true;
>            break;
>          }
>      }
>      return NoSerialize;
> }
>    /// the only template in here. only one instances per 
> serialzed-root-type
>    const(ubyte[]) serialize(T)(T value) {
>      static immutable Type type = cast(immutable Type) nodeFromName("T");
>      return serializeType(cast(ubyte*)&value, type);
>    }
> 
>    const(ubyte[]) serializeType(const ubyte* ptr, const Type type)
>    {
>      // writeShallowTypeDescriptor(Type);
>      // printf("Serializing type: %s\n", type.identifier.ptr);
>      if (auto sa = cast (TypeArray) type)
>      {
>          ulong length = sa.dim;
>          const void* values = ptr;
>          auto elemType = sa.nextOf;
>          return serializeArray(length, ptr, elemType);
>      }
>      else if (auto da = cast(TypeSlice) type)
>      {
>          ulong length = *cast(size_t*) ptr;
>          const void* values = *cast(const ubyte**)(ptr + size_t.sizeof);
>          auto elemType = da.nextOf;
>          return serializeArray(length, ptr, elemType);
>      }
>      else if (auto ts = cast(TypeStruct) type)
>      {
>          auto fields = ts.sym.fields;
>          return serializeAggregate(ptr, fields);
>      }
>      else if (auto tc = cast(TypeClass) type)
>      {
>          auto fields = tc.sym.fields;
>          return serializeAggregate(*cast(const ubyte**)ptr, fields);
>      }
>      else if (auto tb = cast(TypeBasic) type)
>      {
>          // writeTypeTag ?
>          return ptr[0 .. type.size];
>      }
>      else assert(0, "Serialisation for " ~ type.identifier ~ " not 
> implemented .. " ~ (cast()type).toString());
>    }
> 
>    ubyte[] serializeArray(ulong length, const ubyte* ptr, const Type 
> elemType)
>    {
>      ubyte[] result;
>      // writeLength(length)
>      foreach(i; 0 .. length)
>      {
>        result ~= serializeType(ptr, elemType);
>      }
>      return result;
>    }
> 
>    ubyte[] serializeAggregate(const ubyte* ptr, VariableDeclaration[] 
> fields)
>    {
>      ubyte[] result;
>      foreach(f; fields)
>      {
>        if (hasNoSerializeAttrib(f))
>        {
>          // skip fields which are annotated with noSerialize;
>          continue;
>        }
>        result ~= serializeType(ptr + f.offset, f.type);
>      }
>      return result;
>    }
> 
> 
> void main()
> {
>      S s = S(72, 19992034, 98);
>      C c = new C(72, 19992039, 98);
>      auto buffer = serialize(s);
>      assert(buffer.length == 5);
>      assert ((buffer[0] | buffer[1] << 8 | buffer[2] << 16 | buffer[3] 
> << 24) == 19992034);
>      assert (buffer[4] == 98);
>      auto buffer_c = serialize(c);
>      assert(buffer_c.length == 4);
>      assert ((buffer_c[0] | buffer_c[1] << 8 | buffer_c[2] << 16 | 
> buffer_c[3] << 24) == 19992039);
>      // we can see the fields annotated with @("NoSerialize") are skipped
> }
> ```
> 
> I would like to know what you think about this
> 
> In my next example I will show how you can modify serialization for 
> library types source-code you can't control.
> 
> However for that to work `core.reflect` needs to be extended a little ;)
> 
> Cheers and have a nice day,
> 
> Stefan

I'm impressed by your work. But I have one question - when ordinary 
people like me can be able to use all these amazing things you did? 
Iterating over aggregate members considering their type, attributes and 
runtime value is so often in my practice that I'm really interesting in 
your core.reflect but I can use only official compiler w/o any 
customization.


More information about the Digitalmars-d mailing list