zserio icon indicating copy to clipboard operation
zserio copied to clipboard

Consider to split generated objects to immutable and mutable parts

Open mikir opened this issue 4 years ago • 0 comments

Currently, it is possible to generate immutable reader part using command line argument -withoutWriterCode.

However, application which needs to modify data cannot use this immutable reader part (must use "all-in-one" objects which are generated using command line argument -withWriterCode). This makes possible to create an incomplete objects.

This issue is about to split generated objects in C++ and Java to immutable and mutable parts. This will bring must more clear design and will allow some solutions which are currently not possible:

  • For C++, parameters are hold by raw pointers not to force applications which use only immutable reader part to use slow shared pointers.
  • For C++, there is a checking code in immutable part of choices or unions which never throws:
::choice_types::enum_param_choice::Selector EnumParamChoice::getSelector() const
{
    if (!m_isInitialized)
        throw ::zserio::CppRuntimeException("Parameter selector of compound EnumParamChoice "
                "is not initialized!");

    return m_selector_;
}

Hence, the generated objects should be immutable using Builder pattern.

Example:

package structure_types.simple_structure;

struct SimpleStructure
{
    bit:3       numberA;
    uint8       numberB;
    bit:7       numberC;
};

Generated Java code with immutable part:

package structure_types.simple_structure;

public class SimpleStructure implements zserio.runtime.io.InitializeOffsetsWriter, zserio.runtime.SizeOf
{
    public SimpleStructure(zserio.runtime.io.BitStreamReader in)
            throws java.io.IOException, zserio.runtime.ZserioError
    {
        this.numberA_ = (byte)in.readBits(3);
        this.numberB_ = in.readUnsignedByte();
        this.numberC_ = (byte)in.readBits(7);
    }

    public SimpleStructure(
        byte numberA_,
        short numberB_,
        byte numberC_)
    {
        setNumberA(numberA_);
        setNumberB(numberB_);
        setNumberC(numberC_);
    }

    @Override
    public int bitSizeOf(long bitPosition)
    {
        long endBitPosition = bitPosition;

        endBitPosition += 3;
        endBitPosition += 8;
        endBitPosition += 7;

        return (int)(endBitPosition - bitPosition);
    }

    public byte getNumberA()
    {
        return this.numberA_;
    }

    public short getNumberB()
    {
        return this.numberB_;
    }

    public byte getNumberC()
    {
        return this.numberC_;
    }

    @Override
    public boolean equals(java.lang.Object obj)
    {
        if (obj instanceof SimpleStructure)
        {
            final SimpleStructure that = (SimpleStructure)obj;

            return
                    this.numberA_ == that.numberA_ &&
                    this.numberB_ == that.numberB_ &&
                    this.numberC_ == that.numberC_;
        }

        return false;
    }

    @Override
    public int hashCode()
    {
        int result = zserio.runtime.Util.HASH_SEED;

        result = zserio.runtime.Util.HASH_PRIME_NUMBER * result + this.numberA_;
        result = zserio.runtime.Util.HASH_PRIME_NUMBER * result + this.numberB_;
        result = zserio.runtime.Util.HASH_PRIME_NUMBER * result + this.numberC_;

        return result;
    }

    @Override
    public void write(zserio.runtime.io.BitStreamWriter out)
            throws java.io.IOException, zserio.runtime.ZserioError
    {
        out.writeBits(getNumberA(), 3);
        out.writeUnsignedByte(getNumberB());
        out.writeBits(getNumberC(), 7);
    }

    public static class Builder
    {
        ...
    }

    private byte numberA_;
    private short numberB_;
    private byte numberC_;
}

Some notes:

  • Builder must use always Builders for all nested objects.
  • Builder in its method build() will automatically initialize all possible offsets.
  • If offsets are used, build() method will have to be called on top level builder to build an object with correctly initialized offsets,
  • Immutable part will contain write() method compared to the current solution where immutable part is "for readers only". This will suit better for service and pubsub subsystems. Currently, these subsystems can be used only and only if writer code is enabled.

mikir avatar Apr 30 '20 11:04 mikir