1. Packable

Packable trait (importable from std::net::packet) defines two methods pack and unpack. There is a default behavior for those two methods, and it is recommended to not override them, unless you know exactly what you are doing. The pack method takes a immutable object instance as parameter and creates a packet of [u8], encoding the object. This packet can then be sent throw network, and unpack from another processes.

import std::io;
import std::net::packet;

class Foo {
    let _v : [c8];

    pub self (v : [c8]) with _v = v {}

    impl Packable;
}


def main () {
    let a = Foo::new ("Test"s8);

    let packet = a.pack ();
    println (packet);
}


Results :

[9, 0, 0, 0, 0, 0, 0, 0, 34, 6d, 61, 69, 6e, 33, 46, 6f, 6f, 4, 0, 0, 0, 0, 0, 0, 0, 54, 65, 73, 74]


The packet can then be unpacked with the function unpack. This function returns an Object, that can be transformed in the appropriate type using pattern matching.

    {
        match unpack (packet) {
            x : &Foo  => println (x);
            _ => {
                println ("Unkown packet");
            }
        }
    } catch {
        UnpackError () => {
            println ("The packet contains unknwon information");
        }
    }


Results:

main::Foo(Test)


Warning to work properly, the unpacker must have access to the type information of the object that is packed. Otherwise, an UnpackError is thrown. To make the symbol available in the unpacking program, the class must be compiled and linked in it. For example, if a module foo contains a class Foo, and the unpacking is made inside the **main module. The command line of the compilation must be :

gyc main.yr foo.yr


It is possible to verify that the symbol are present in the executable, by running the following command (example for the class Foo of the previous example).

$ objdump -t a.out | grep Foo
0000000000410585 g     F .text    00000000000004a0              _Y3std3net6packet8Packable27__stdnetwork__unpackContentFxP9x3foo3FooSu8Zusize
0000000000447300  w    O .data    0000000000000030              _Y3foo3FooTI
0000000000410a25 g     F .text    000000000000006e              _Y3std3net6packet8Packable25__stdnetwork__packContentFP83foo3FooxP32x3std10collection3vec6VecNu83VecZv
000000000040eb82 g     F .text    0000000000000054              _Y3foo3Foo4selfFxP9x3foo3FooSc8ZxP9x3foo3Foo
00000000004472f0  w    O .data    0000000000000010              _Y133foo3Foo_nameCSTxSc32
0000000000447340  w    O .data    0000000000000030              _Y3foo3FooVT
000000000040f468 g     F .text    0000000000000093              _Y3std3net6packet8Packable4packFP83foo3FooxP32x3std10collection3vec6VecNu83VecZv
000000000040f381 g     F .text    00000000000000e7              _Y3std3net6packet8Packable4packFP83foo3FooZSu8
00000000004472c0  w    O .data    0000000000000024              _Y183foo3Foo_nameInnerCSTxA9c32


In the result of this command, the symbol _Y3foo3FooTI is present, this is the symbol containing the type info of the foo::Foo class, _Y3foo3FooVT contains the vtable, and _Y3std3net6packet8Packable27__stdnetwork__unpackContentFxP9x3foo3FooSu8Zusize the function called to unpack the object. These are the three mandatory symbols to successfully unpack a object (the other symbols are necessarily there if the vtable is present).

I think this is not a strong problem, as it can be easily resolved; but must be taken into account when compiling the program.

Contribution: Almost every types are packable. I think the only two types that are not are function pointer and closure. I see how function pointer packing can be done, and it is not difficult and will be done in future version of the std. On the other hand, because the type info of the closure is not accessible, i don't see any way of packing this type. In any case, closure behavior can be easily simulated by using a object instance, or a structure and a function pointer. And trying to pack a not packable type creates compile time errors, so there is no bad surprise at runtime.

1.1. Serializable

Like Packable trait, Serializable is a trait that transform an object instance into something that can be stored, or sent. However, unlike Packable, the result is humanely readable, and can be used to create configuration files for example (current std implements toml and json serialization). Serializable objects implements the method serialize. It has no default behavior (Contribution it is however probably possible to create a default behavior based on the name of the fields). This method takes an immutable object instance, and return a &Config value.

import std::io;
import std::config::conv;
import std::config, std::config::toml;


struct
| A : &Foo
| B : &Foo
 -> Bar;

class Foo {
    let _v : [c32];
    let _u : i32;

    pub self (v : [c32], u : i32) with _v = v, _u = u{
    }

    impl Serializable {
        pub over serialize (self)->  &Config {
            let dmut d = Dict::new ();
            d:.insert ("v", self._v.to!(&Config) ());
            d:.insert ("u", self._u.to!(&Config) ());

            d
        }
    }
}


def main () {
    let x = Bar (Foo::new ("Test", 12), Foo::new ("Test2", 34));    
    println (toml::dump (x.to!(&Config) ()));
}


Results :


[A]
u = 12
v = "Test"

[B]
u = 34
v = "Test2"

results matching ""

    No results matching ""