class AutoC::Box

C union wrapper with managed fields

Attributes

tag_[R]
variants[R]

Public Class Methods

new(type, variants, visibility: :public) click to toggle source
Calls superclass method
# File lib/autoc/box.rb, line 29
def initialize(type, variants, visibility: :public)
  super(type, visibility:)
  setup_variants(variants)
  @tag_ = "#{signature}_";
  @default = 'abort();'
end

Public Instance Methods

comparable?(= variants.values.all? { |t| t.comparable? }) click to toggle source
# File lib/autoc/box.rb, line 24
  def comparable? = variants.values.all? { |t| t.comparable? }
  def orderable? = variants.values.all? { |t| t.orderable? }
  def copyable? = variants.values.all? { |t| t.copyable? }
  def hashable? = variants.values.all? { |t| t.hashable? }

  def initialize(type, variants, visibility: :public)
    super(type, visibility:)
    setup_variants(variants)
    @tag_ = "#{signature}_";
    @default = 'abort();'
  end

  def render_interface(stream)
    if public?
      stream << %{
        /**
          #{defgroup}
          @brief Value type wrapper of the C union
        */
      }
      stream << %{
        /**
          #{ingroup}

          @brief Box tag set

          Use @ref #{identifier(:tag)} to query current box contents. Empty box is always tagged as 0.

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef enum {'
    i = 0; stream << (variants.collect { |name, type| "#{identifier(name)} = #{i+=1}" }).join(',')
    stream << "} #{tag_};"
    if public?
      stream << %{
        /**
          #{ingroup}

          @brief Opaque struct holding state of the box

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef struct {union {'
      variants.each { |name, type| stream << field_declaration(type, name) }
    stream << "} variant; /**< @private */ #{tag_} tag; /**< @private */} #{signature};"
  end

  def type_tag = "#{signature}<#{variants.values.join(',')}>"

private

  # @private
  def setup_variants(variants)
    @variants = variants.transform_values { |type| type.to_type }
    self.variants.each_value { |type| dependencies << type }
  end

  # @private
  def field_variable(opt)
    if opt.is_a?(::Hash)
      obj, name = opt.first
      "#{obj}->#{name}"
    else
      opt
    end
  end

  # @private
  def field_declaration(type, name)
    s = "#{type} #{field_variable(name)};"
    s += '/**< @private */' if @opaque
    s
  end

  def configure
    super
    default_create.configure do
      inline_code %{
        assert(target);
        target->tag = (#{tag_})0;
      }
    end
    method(tag_, :tag, { target: const_rvalue }).configure do
      inline_code %{
        assert(target);
        return target->tag;
      }
      header %{
        @brief Get type of contained element

        @param[in] target box to query
        @return tag of currently contained element

        This function returns a tag of currently contained element (@ref #{tag_}) or zero value if the box is empty (i.e. contains nothing).

        @since 2.0
      }
    end
    method(:void, :purge, { target: rvalue }).configure do
      code %{
        assert(target);
        #{destroy.(target) if destructible?};
        #{default_create.(target)};
      }
      header %{
        @brief Reset box

        @param[in] target box to purge

        This function resets the box by destroying containing element (if any).
        The box is left empty.

        @since 2.0
      }
    end
    ### destroy
      _code = %$
        assert(target);
        if(target->tag) switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.destroy.("target->variant.#{name}") if type.destructible?
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      destroy.configure { code _code }
    ### copy
      _code = %$
        assert(target);
        assert(source);
        if(source->tag) switch(source->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.copy.("target->variant.#{name}", "source->variant.#{name}")
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '} target->tag = source->tag;'
      copy.configure { code _code }
    ### equal
      _code = %$
        assert(left);
        assert(right);
        if(left->tag != right->tag) return 0;
        if(!left->tag) return 1;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += 'return '
        _code += type.equal.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      equal.configure { code _code }
    ### compare
      _code = %$
        assert(left);
        assert(right);
        assert(left->tag == right->tag);
        if(!left->tag) return 0;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.compare.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      compare.configure { code _code }
    ### hash_code
      _code = %$
        assert(target);
        if(!target->tag) return AUTOC_HASHER_SEED;
        switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.hash_code.("target->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      hash_code.configure { code _code }
    ### typed accessors
    _type = self
    variants.each do |name, type|
      method(type.const_lvalue, "view_#{name}", { target: const_rvalue }).configure do
        code %{
          assert(target);
          assert(target->tag == #{identifier(name)});
          return &target->variant.#{name};
        }
        header %{
          @brief Get a view of contained value of type #{type}

          @param[in] target box to query
          @return a view of the value of type #{type}

          This function is used to get a constant reference (in form of the C pointer) to value contained in `target`.

          It is the caller's responsibility to check the type of currently contained value.
          Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

          @see @ref #{tag}

          @since 2.0
        }
      end
      if type.copyable?
        method(type, "get_#{name}", { target: const_rvalue }).configure do
          code %{
            #{type} result;
            assert(target);
            assert(target->tag == #{identifier(name)});
            #{type.copy.(:result, "target->variant.#{name}")};
            return result;
          }
          header %{
            @brief Get a copy of contained value of type #{type}

            @param[in] target box to query
            @return a copy of the value of type #{type}

            This function is used to get a copy of the value contained in `target`.

            It is the caller's responsibility to check the type of currently contained value.
            Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

            @see @ref #{tag}

            @since 2.0
          }
        end
        method(:void, "put_#{name}", { target: rvalue, value: type.const_rvalue }).configure do
          code %{
            assert(target);
            #{destroy.(target) if destructible?};
            #{type.copy.("target->variant.#{name}", value)};
            target->tag = #{identifier(name)};
          }
          header %{
            @brief Put value of type #{type} into box

            @param[in] target box to set
            @param[in] value value of type #{type} to put

            This function is used to put a copy of the value into the box.
            Previously contained value is destroyed with respective destructor.

            After call to this function the value type may be obtained with @ref #{identifier(:tag)}.

            @see @ref #{tag}

            @since 2.0
          }
        end
      end
    end
  end

configure() click to toggle source
Calls superclass method
# File lib/autoc/box.rb, line 106
def configure
  super
  default_create.configure do
    inline_code %{
      assert(target);
      target->tag = (#{tag_})0;
    }
  end
  method(tag_, :tag, { target: const_rvalue }).configure do
    inline_code %{
      assert(target);
      return target->tag;
    }
    header %{
      @brief Get type of contained element

      @param[in] target box to query
      @return tag of currently contained element

      This function returns a tag of currently contained element (@ref #{tag_}) or zero value if the box is empty (i.e. contains nothing).

      @since 2.0
    }
  end
  method(:void, :purge, { target: rvalue }).configure do
    code %{
      assert(target);
      #{destroy.(target) if destructible?};
      #{default_create.(target)};
    }
    header %{
      @brief Reset box

      @param[in] target box to purge

      This function resets the box by destroying containing element (if any).
      The box is left empty.

      @since 2.0
    }
  end
  ### destroy
    _code = %$
      assert(target);
      if(target->tag) switch(target->tag) {
    $
    variants.each do |name, type|
      _code += "case #{identifier(name)}:"
      _code += type.destroy.("target->variant.#{name}") if type.destructible?
      _code += ';break;'
    end
    _code += %{
      default: #{@default}
    }
    _code += '}'
    destroy.configure { code _code }
  ### copy
    _code = %$
      assert(target);
      assert(source);
      if(source->tag) switch(source->tag) {
    $
    variants.each do |name, type|
      _code += "case #{identifier(name)}:"
      _code += type.copy.("target->variant.#{name}", "source->variant.#{name}")
      _code += ';break;'
    end
    _code += %{
      default: #{@default}
    }
    _code += '} target->tag = source->tag;'
    copy.configure { code _code }
  ### equal
    _code = %$
      assert(left);
      assert(right);
      if(left->tag != right->tag) return 0;
      if(!left->tag) return 1;
      switch(left->tag) {
    $
    variants.each do |name, type|
      _code += "case #{identifier(name)}:"
      _code += 'return '
      _code += type.equal.("left->variant.#{name}", "right->variant.#{name}")
      _code += ';'
    end
    _code += %{
      default: #{@default}
    }
    _code += '}'
    equal.configure { code _code }
  ### compare
    _code = %$
      assert(left);
      assert(right);
      assert(left->tag == right->tag);
      if(!left->tag) return 0;
      switch(left->tag) {
    $
    variants.each do |name, type|
      _code += "case #{identifier(name)}: return "
      _code += type.compare.("left->variant.#{name}", "right->variant.#{name}")
      _code += ';'
    end
    _code += %{
      default: #{@default}
    }
    _code += '}'
    compare.configure { code _code }
  ### hash_code
    _code = %$
      assert(target);
      if(!target->tag) return AUTOC_HASHER_SEED;
      switch(target->tag) {
    $
    variants.each do |name, type|
      _code += "case #{identifier(name)}: return "
      _code += type.hash_code.("target->variant.#{name}")
      _code += ';'
    end
    _code += %{
      default: #{@default}
    }
    _code += '}'
    hash_code.configure { code _code }
  ### typed accessors
  _type = self
  variants.each do |name, type|
    method(type.const_lvalue, "view_#{name}", { target: const_rvalue }).configure do
      code %{
        assert(target);
        assert(target->tag == #{identifier(name)});
        return &target->variant.#{name};
      }
      header %{
        @brief Get a view of contained value of type #{type}

        @param[in] target box to query
        @return a view of the value of type #{type}

        This function is used to get a constant reference (in form of the C pointer) to value contained in `target`.

        It is the caller's responsibility to check the type of currently contained value.
        Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

        @see @ref #{tag}

        @since 2.0
      }
    end
    if type.copyable?
      method(type, "get_#{name}", { target: const_rvalue }).configure do
        code %{
          #{type} result;
          assert(target);
          assert(target->tag == #{identifier(name)});
          #{type.copy.(:result, "target->variant.#{name}")};
          return result;
        }
        header %{
          @brief Get a copy of contained value of type #{type}
  
          @param[in] target box to query
          @return a copy of the value of type #{type}
  
          This function is used to get a copy of the value contained in `target`.
  
          It is the caller's responsibility to check the type of currently contained value.
          Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`
  
          @see @ref #{tag}
  
          @since 2.0
        }
      end
      method(:void, "put_#{name}", { target: rvalue, value: type.const_rvalue }).configure do
        code %{
          assert(target);
          #{destroy.(target) if destructible?};
          #{type.copy.("target->variant.#{name}", value)};
          target->tag = #{identifier(name)};
        }
        header %{
          @brief Put value of type #{type} into box
  
          @param[in] target box to set
          @param[in] value value of type #{type} to put
  
          This function is used to put a copy of the value into the box.
          Previously contained value is destroyed with respective destructor.
  
          After call to this function the value type may be obtained with @ref #{identifier(:tag)}.

          @see @ref #{tag}
  
          @since 2.0
        }
      end
    end
  end
end
copyable?(= variants.values.all? { |t| t.copyable? }) click to toggle source
# File lib/autoc/box.rb, line 26
  def copyable? = variants.values.all? { |t| t.copyable? }
  def hashable? = variants.values.all? { |t| t.hashable? }

  def initialize(type, variants, visibility: :public)
    super(type, visibility:)
    setup_variants(variants)
    @tag_ = "#{signature}_";
    @default = 'abort();'
  end

  def render_interface(stream)
    if public?
      stream << %{
        /**
          #{defgroup}
          @brief Value type wrapper of the C union
        */
      }
      stream << %{
        /**
          #{ingroup}

          @brief Box tag set

          Use @ref #{identifier(:tag)} to query current box contents. Empty box is always tagged as 0.

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef enum {'
    i = 0; stream << (variants.collect { |name, type| "#{identifier(name)} = #{i+=1}" }).join(',')
    stream << "} #{tag_};"
    if public?
      stream << %{
        /**
          #{ingroup}

          @brief Opaque struct holding state of the box

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef struct {union {'
      variants.each { |name, type| stream << field_declaration(type, name) }
    stream << "} variant; /**< @private */ #{tag_} tag; /**< @private */} #{signature};"
  end

  def type_tag = "#{signature}<#{variants.values.join(',')}>"

private

  # @private
  def setup_variants(variants)
    @variants = variants.transform_values { |type| type.to_type }
    self.variants.each_value { |type| dependencies << type }
  end

  # @private
  def field_variable(opt)
    if opt.is_a?(::Hash)
      obj, name = opt.first
      "#{obj}->#{name}"
    else
      opt
    end
  end

  # @private
  def field_declaration(type, name)
    s = "#{type} #{field_variable(name)};"
    s += '/**< @private */' if @opaque
    s
  end

  def configure
    super
    default_create.configure do
      inline_code %{
        assert(target);
        target->tag = (#{tag_})0;
      }
    end
    method(tag_, :tag, { target: const_rvalue }).configure do
      inline_code %{
        assert(target);
        return target->tag;
      }
      header %{
        @brief Get type of contained element

        @param[in] target box to query
        @return tag of currently contained element

        This function returns a tag of currently contained element (@ref #{tag_}) or zero value if the box is empty (i.e. contains nothing).

        @since 2.0
      }
    end
    method(:void, :purge, { target: rvalue }).configure do
      code %{
        assert(target);
        #{destroy.(target) if destructible?};
        #{default_create.(target)};
      }
      header %{
        @brief Reset box

        @param[in] target box to purge

        This function resets the box by destroying containing element (if any).
        The box is left empty.

        @since 2.0
      }
    end
    ### destroy
      _code = %$
        assert(target);
        if(target->tag) switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.destroy.("target->variant.#{name}") if type.destructible?
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      destroy.configure { code _code }
    ### copy
      _code = %$
        assert(target);
        assert(source);
        if(source->tag) switch(source->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.copy.("target->variant.#{name}", "source->variant.#{name}")
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '} target->tag = source->tag;'
      copy.configure { code _code }
    ### equal
      _code = %$
        assert(left);
        assert(right);
        if(left->tag != right->tag) return 0;
        if(!left->tag) return 1;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += 'return '
        _code += type.equal.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      equal.configure { code _code }
    ### compare
      _code = %$
        assert(left);
        assert(right);
        assert(left->tag == right->tag);
        if(!left->tag) return 0;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.compare.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      compare.configure { code _code }
    ### hash_code
      _code = %$
        assert(target);
        if(!target->tag) return AUTOC_HASHER_SEED;
        switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.hash_code.("target->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      hash_code.configure { code _code }
    ### typed accessors
    _type = self
    variants.each do |name, type|
      method(type.const_lvalue, "view_#{name}", { target: const_rvalue }).configure do
        code %{
          assert(target);
          assert(target->tag == #{identifier(name)});
          return &target->variant.#{name};
        }
        header %{
          @brief Get a view of contained value of type #{type}

          @param[in] target box to query
          @return a view of the value of type #{type}

          This function is used to get a constant reference (in form of the C pointer) to value contained in `target`.

          It is the caller's responsibility to check the type of currently contained value.
          Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

          @see @ref #{tag}

          @since 2.0
        }
      end
      if type.copyable?
        method(type, "get_#{name}", { target: const_rvalue }).configure do
          code %{
            #{type} result;
            assert(target);
            assert(target->tag == #{identifier(name)});
            #{type.copy.(:result, "target->variant.#{name}")};
            return result;
          }
          header %{
            @brief Get a copy of contained value of type #{type}

            @param[in] target box to query
            @return a copy of the value of type #{type}

            This function is used to get a copy of the value contained in `target`.

            It is the caller's responsibility to check the type of currently contained value.
            Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

            @see @ref #{tag}

            @since 2.0
          }
        end
        method(:void, "put_#{name}", { target: rvalue, value: type.const_rvalue }).configure do
          code %{
            assert(target);
            #{destroy.(target) if destructible?};
            #{type.copy.("target->variant.#{name}", value)};
            target->tag = #{identifier(name)};
          }
          header %{
            @brief Put value of type #{type} into box

            @param[in] target box to set
            @param[in] value value of type #{type} to put

            This function is used to put a copy of the value into the box.
            Previously contained value is destroyed with respective destructor.

            After call to this function the value type may be obtained with @ref #{identifier(:tag)}.

            @see @ref #{tag}

            @since 2.0
          }
        end
      end
    end
  end

end # Box

custom_constructible?(= false) click to toggle source
# File lib/autoc/box.rb, line 22
  def custom_constructible? = false
  def destructible? = variants.values.any? { |t| t.destructible? }
  def comparable? = variants.values.all? { |t| t.comparable? }
  def orderable? = variants.values.all? { |t| t.orderable? }
  def copyable? = variants.values.all? { |t| t.copyable? }
  def hashable? = variants.values.all? { |t| t.hashable? }

  def initialize(type, variants, visibility: :public)
    super(type, visibility:)
    setup_variants(variants)
    @tag_ = "#{signature}_";
    @default = 'abort();'
  end

  def render_interface(stream)
    if public?
      stream << %{
        /**
          #{defgroup}
          @brief Value type wrapper of the C union
        */
      }
      stream << %{
        /**
          #{ingroup}

          @brief Box tag set

          Use @ref #{identifier(:tag)} to query current box contents. Empty box is always tagged as 0.

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef enum {'
    i = 0; stream << (variants.collect { |name, type| "#{identifier(name)} = #{i+=1}" }).join(',')
    stream << "} #{tag_};"
    if public?
      stream << %{
        /**
          #{ingroup}

          @brief Opaque struct holding state of the box

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef struct {union {'
      variants.each { |name, type| stream << field_declaration(type, name) }
    stream << "} variant; /**< @private */ #{tag_} tag; /**< @private */} #{signature};"
  end

  def type_tag = "#{signature}<#{variants.values.join(',')}>"

private

  # @private
  def setup_variants(variants)
    @variants = variants.transform_values { |type| type.to_type }
    self.variants.each_value { |type| dependencies << type }
  end

  # @private
  def field_variable(opt)
    if opt.is_a?(::Hash)
      obj, name = opt.first
      "#{obj}->#{name}"
    else
      opt
    end
  end

  # @private
  def field_declaration(type, name)
    s = "#{type} #{field_variable(name)};"
    s += '/**< @private */' if @opaque
    s
  end

  def configure
    super
    default_create.configure do
      inline_code %{
        assert(target);
        target->tag = (#{tag_})0;
      }
    end
    method(tag_, :tag, { target: const_rvalue }).configure do
      inline_code %{
        assert(target);
        return target->tag;
      }
      header %{
        @brief Get type of contained element

        @param[in] target box to query
        @return tag of currently contained element

        This function returns a tag of currently contained element (@ref #{tag_}) or zero value if the box is empty (i.e. contains nothing).

        @since 2.0
      }
    end
    method(:void, :purge, { target: rvalue }).configure do
      code %{
        assert(target);
        #{destroy.(target) if destructible?};
        #{default_create.(target)};
      }
      header %{
        @brief Reset box

        @param[in] target box to purge

        This function resets the box by destroying containing element (if any).
        The box is left empty.

        @since 2.0
      }
    end
    ### destroy
      _code = %$
        assert(target);
        if(target->tag) switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.destroy.("target->variant.#{name}") if type.destructible?
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      destroy.configure { code _code }
    ### copy
      _code = %$
        assert(target);
        assert(source);
        if(source->tag) switch(source->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.copy.("target->variant.#{name}", "source->variant.#{name}")
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '} target->tag = source->tag;'
      copy.configure { code _code }
    ### equal
      _code = %$
        assert(left);
        assert(right);
        if(left->tag != right->tag) return 0;
        if(!left->tag) return 1;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += 'return '
        _code += type.equal.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      equal.configure { code _code }
    ### compare
      _code = %$
        assert(left);
        assert(right);
        assert(left->tag == right->tag);
        if(!left->tag) return 0;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.compare.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      compare.configure { code _code }
    ### hash_code
      _code = %$
        assert(target);
        if(!target->tag) return AUTOC_HASHER_SEED;
        switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.hash_code.("target->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      hash_code.configure { code _code }
    ### typed accessors
    _type = self
    variants.each do |name, type|
      method(type.const_lvalue, "view_#{name}", { target: const_rvalue }).configure do
        code %{
          assert(target);
          assert(target->tag == #{identifier(name)});
          return &target->variant.#{name};
        }
        header %{
          @brief Get a view of contained value of type #{type}

          @param[in] target box to query
          @return a view of the value of type #{type}

          This function is used to get a constant reference (in form of the C pointer) to value contained in `target`.

          It is the caller's responsibility to check the type of currently contained value.
          Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

          @see @ref #{tag}

          @since 2.0
        }
      end
      if type.copyable?
        method(type, "get_#{name}", { target: const_rvalue }).configure do
          code %{
            #{type} result;
            assert(target);
            assert(target->tag == #{identifier(name)});
            #{type.copy.(:result, "target->variant.#{name}")};
            return result;
          }
          header %{
            @brief Get a copy of contained value of type #{type}

            @param[in] target box to query
            @return a copy of the value of type #{type}

            This function is used to get a copy of the value contained in `target`.

            It is the caller's responsibility to check the type of currently contained value.
            Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

            @see @ref #{tag}

            @since 2.0
          }
        end
        method(:void, "put_#{name}", { target: rvalue, value: type.const_rvalue }).configure do
          code %{
            assert(target);
            #{destroy.(target) if destructible?};
            #{type.copy.("target->variant.#{name}", value)};
            target->tag = #{identifier(name)};
          }
          header %{
            @brief Put value of type #{type} into box

            @param[in] target box to set
            @param[in] value value of type #{type} to put

            This function is used to put a copy of the value into the box.
            Previously contained value is destroyed with respective destructor.

            After call to this function the value type may be obtained with @ref #{identifier(:tag)}.

            @see @ref #{tag}

            @since 2.0
          }
        end
      end
    end
  
default_constructible?(= true) click to toggle source
# File lib/autoc/box.rb, line 21
  def default_constructible? = true
  def custom_constructible? = false
  def destructible? = variants.values.any? { |t| t.destructible? }
  def comparable? = variants.values.all? { |t| t.comparable? }
  def orderable? = variants.values.all? { |t| t.orderable? }
  def copyable? = variants.values.all? { |t| t.copyable? }
  def hashable? = variants.values.all? { |t| t.hashable? }

  def initialize(type, variants, visibility: :public)
    super(type, visibility:)
    setup_variants(variants)
    @tag_ = "#{signature}_";
    @default = 'abort();'
  end

  def render_interface(stream)
    if public?
      stream << %{
        /**
          #{defgroup}
          @brief Value type wrapper of the C union
        */
      }
      stream << %{
        /**
          #{ingroup}

          @brief Box tag set

          Use @ref #{identifier(:tag)} to query current box contents. Empty box is always tagged as 0.

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef enum {'
    i = 0; stream << (variants.collect { |name, type| "#{identifier(name)} = #{i+=1}" }).join(',')
    stream << "} #{tag_};"
    if public?
      stream << %{
        /**
          #{ingroup}

          @brief Opaque struct holding state of the box

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef struct {union {'
      variants.each { |name, type| stream << field_declaration(type, name) }
    stream << "} variant; /**< @private */ #{tag_} tag; /**< @private */} #{signature};"
  end

  def type_tag = "#{signature}<#{variants.values.join(',')}>"

private

  # @private
  def setup_variants(variants)
    @variants = variants.transform_values { |type| type.to_type }
    self.variants.each_value { |type| dependencies << type }
  end

  # @private
  def field_variable(opt)
    if opt.is_a?(::Hash)
      obj, name = opt.first
      "#{obj}->#{name}"
    else
      opt
    end
  end

  # @private
  def field_declaration(type, name)
    s = "#{type} #{field_variable(name)};"
    s += '/**< @private */' if @opaque
    s
  end

  def configure
    super
    default_create.configure do
      inline_code %{
        assert(target);
        target->tag = (#{tag_})0;
      }
    end
    method(tag_, :tag, { target: const_rvalue }).configure do
      inline_code %{
        assert(target);
        return target->tag;
      }
      header %{
        @brief Get type of contained element

        @param[in] target box to query
        @return tag of currently contained element

        This function returns a tag of currently contained element (@ref #{tag_}) or zero value if the box is empty (i.e. contains nothing).

        @since 2.0
      }
    end
    method(:void, :purge, { target: rvalue }).configure do
      code %{
        assert(target);
        #{destroy.(target) if destructible?};
        #{default_create.(target)};
      }
      header %{
        @brief Reset box

        @param[in] target box to purge

        This function resets the box by destroying containing element (if any).
        The box is left empty.

        @since 2.0
      }
    end
    ### destroy
      _code = %$
        assert(target);
        if(target->tag) switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.destroy.("target->variant.#{name}") if type.destructible?
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      destroy.configure { code _code }
    ### copy
      _code = %$
        assert(target);
        assert(source);
        if(source->tag) switch(source->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.copy.("target->variant.#{name}", "source->variant.#{name}")
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '} target->tag = source->tag;'
      copy.configure { code _code }
    ### equal
      _code = %$
        assert(left);
        assert(right);
        if(left->tag != right->tag) return 0;
        if(!left->tag) return 1;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += 'return '
        _code += type.equal.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      equal.configure { code _code }
    ### compare
      _code = %$
        assert(left);
        assert(right);
        assert(left->tag == right->tag);
        if(!left->tag) return 0;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.compare.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      compare.configure { code _code }
    ### hash_code
      _code = %$
        assert(target);
        if(!target->tag) return AUTOC_HASHER_SEED;
        switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.hash_code.("target->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      hash_code.configure { code _code }
    ### typed accessors
    _type = self
    variants.each do |name, type|
      method(type.const_lvalue, "view_#{name}", { target: const_rvalue }).configure do
        code %{
          assert(target);
          assert(target->tag == #{identifier(name)});
          return &target->variant.#{name};
        }
        header %{
          @brief Get a view of contained value of type #{type}

          @param[in] target box to query
          @return a view of the value of type #{type}

          This function is used to get a constant reference (in form of the C pointer) to value contained in `target`.

          It is the caller's responsibility to check the type of currently contained value.
          Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

          @see @ref #{tag}

          @since 2.0
        }
      end
      if type.copyable?
        method(type, "get_#{name}", { target: const_rvalue }).configure do
          code %{
            #{type} result;
            assert(target);
            assert(target->tag == #{identifier(name)});
            #{type.copy.(:result, "target->variant.#{name}")};
            return result;
          }
          header %{
            @brief Get a copy of contained value of type #{type}

            @param[in] target box to query
            @return a copy of the value of type #{type}

            This function is used to get a copy of the value contained in `target`.

            It is the caller's responsibility to check the type of currently contained value.
            Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

            @see @ref #{tag}

            @since 2.0
          }
        end
        method(:void, "put_#{name}", { target: rvalue, value: type.const_rvalue }).configure do
          code %{
            assert(target);
            #{destroy.(target) if destructible?};
            #{type.copy.("target->variant.#{name}", value)};
            target->tag = #{identifier(name)};
          }
          header %{
            @brief Put value of type #{type} into box

            @param[in] target box to set
            @param[in] value value of type #{type} to put

            This function is used to put a copy of the value into the box.
            Previously contained value is destroyed with respective destructor.

            After call to this function the value type may be obtained with @ref #{identifier(:tag)}.

            @see @ref #{tag}

            @since 2.0
          }
        end
      end
    end
destructible?(= variants.values.any? { |t| t.destructible? }) click to toggle source
# File lib/autoc/box.rb, line 23
  def destructible? = variants.values.any? { |t| t.destructible? }
  def comparable? = variants.values.all? { |t| t.comparable? }
  def orderable? = variants.values.all? { |t| t.orderable? }
  def copyable? = variants.values.all? { |t| t.copyable? }
  def hashable? = variants.values.all? { |t| t.hashable? }

  def initialize(type, variants, visibility: :public)
    super(type, visibility:)
    setup_variants(variants)
    @tag_ = "#{signature}_";
    @default = 'abort();'
  end

  def render_interface(stream)
    if public?
      stream << %{
        /**
          #{defgroup}
          @brief Value type wrapper of the C union
        */
      }
      stream << %{
        /**
          #{ingroup}

          @brief Box tag set

          Use @ref #{identifier(:tag)} to query current box contents. Empty box is always tagged as 0.

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef enum {'
    i = 0; stream << (variants.collect { |name, type| "#{identifier(name)} = #{i+=1}" }).join(',')
    stream << "} #{tag_};"
    if public?
      stream << %{
        /**
          #{ingroup}

          @brief Opaque struct holding state of the box

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef struct {union {'
      variants.each { |name, type| stream << field_declaration(type, name) }
    stream << "} variant; /**< @private */ #{tag_} tag; /**< @private */} #{signature};"
  end

  def type_tag = "#{signature}<#{variants.values.join(',')}>"

private

  # @private
  def setup_variants(variants)
    @variants = variants.transform_values { |type| type.to_type }
    self.variants.each_value { |type| dependencies << type }
  end

  # @private
  def field_variable(opt)
    if opt.is_a?(::Hash)
      obj, name = opt.first
      "#{obj}->#{name}"
    else
      opt
    end
  end

  # @private
  def field_declaration(type, name)
    s = "#{type} #{field_variable(name)};"
    s += '/**< @private */' if @opaque
    s
  end

  def configure
    super
    default_create.configure do
      inline_code %{
        assert(target);
        target->tag = (#{tag_})0;
      }
    end
    method(tag_, :tag, { target: const_rvalue }).configure do
      inline_code %{
        assert(target);
        return target->tag;
      }
      header %{
        @brief Get type of contained element

        @param[in] target box to query
        @return tag of currently contained element

        This function returns a tag of currently contained element (@ref #{tag_}) or zero value if the box is empty (i.e. contains nothing).

        @since 2.0
      }
    end
    method(:void, :purge, { target: rvalue }).configure do
      code %{
        assert(target);
        #{destroy.(target) if destructible?};
        #{default_create.(target)};
      }
      header %{
        @brief Reset box

        @param[in] target box to purge

        This function resets the box by destroying containing element (if any).
        The box is left empty.

        @since 2.0
      }
    end
    ### destroy
      _code = %$
        assert(target);
        if(target->tag) switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.destroy.("target->variant.#{name}") if type.destructible?
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      destroy.configure { code _code }
    ### copy
      _code = %$
        assert(target);
        assert(source);
        if(source->tag) switch(source->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.copy.("target->variant.#{name}", "source->variant.#{name}")
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '} target->tag = source->tag;'
      copy.configure { code _code }
    ### equal
      _code = %$
        assert(left);
        assert(right);
        if(left->tag != right->tag) return 0;
        if(!left->tag) return 1;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += 'return '
        _code += type.equal.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      equal.configure { code _code }
    ### compare
      _code = %$
        assert(left);
        assert(right);
        assert(left->tag == right->tag);
        if(!left->tag) return 0;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.compare.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      compare.configure { code _code }
    ### hash_code
      _code = %$
        assert(target);
        if(!target->tag) return AUTOC_HASHER_SEED;
        switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.hash_code.("target->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      hash_code.configure { code _code }
    ### typed accessors
    _type = self
    variants.each do |name, type|
      method(type.const_lvalue, "view_#{name}", { target: const_rvalue }).configure do
        code %{
          assert(target);
          assert(target->tag == #{identifier(name)});
          return &target->variant.#{name};
        }
        header %{
          @brief Get a view of contained value of type #{type}

          @param[in] target box to query
          @return a view of the value of type #{type}

          This function is used to get a constant reference (in form of the C pointer) to value contained in `target`.

          It is the caller's responsibility to check the type of currently contained value.
          Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

          @see @ref #{tag}

          @since 2.0
        }
      end
      if type.copyable?
        method(type, "get_#{name}", { target: const_rvalue }).configure do
          code %{
            #{type} result;
            assert(target);
            assert(target->tag == #{identifier(name)});
            #{type.copy.(:result, "target->variant.#{name}")};
            return result;
          }
          header %{
            @brief Get a copy of contained value of type #{type}

            @param[in] target box to query
            @return a copy of the value of type #{type}

            This function is used to get a copy of the value contained in `target`.

            It is the caller's responsibility to check the type of currently contained value.
            Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

            @see @ref #{tag}

            @since 2.0
          }
        end
        method(:void, "put_#{name}", { target: rvalue, value: type.const_rvalue }).configure do
          code %{
            assert(target);
            #{destroy.(target) if destructible?};
            #{type.copy.("target->variant.#{name}", value)};
            target->tag = #{identifier(name)};
          }
          header %{
            @brief Put value of type #{type} into box

            @param[in] target box to set
            @param[in] value value of type #{type} to put

            This function is used to put a copy of the value into the box.
            Previously contained value is destroyed with respective destructor.

            After call to this function the value type may be obtained with @ref #{identifier(:tag)}.

            @see @ref #{tag}

            @since 2.0
          }
        end
      end
    end
  end
field_declaration(type, name) click to toggle source

@private

# File lib/autoc/box.rb, line 100
def field_declaration(type, name)
  s = "#{type} #{field_variable(name)};"
  s += '/**< @private */' if @opaque
  s
end
field_variable(opt) click to toggle source

@private

# File lib/autoc/box.rb, line 90
def field_variable(opt)
  if opt.is_a?(::Hash)
    obj, name = opt.first
    "#{obj}->#{name}"
  else
    opt
  end
end
hashable?(= variants.values.all? { |t| t.hashable? }) click to toggle source
# File lib/autoc/box.rb, line 27
    def hashable? = variants.values.all? { |t| t.hashable? }

    def initialize(type, variants, visibility: :public)
      super(type, visibility:)
      setup_variants(variants)
      @tag_ = "#{signature}_";
      @default = 'abort();'
    end

    def render_interface(stream)
      if public?
        stream << %{
          /**
            #{defgroup}
            @brief Value type wrapper of the C union
          */
        }
        stream << %{
          /**
            #{ingroup}

            @brief Box tag set

            Use @ref #{identifier(:tag)} to query current box contents. Empty box is always tagged as 0.

            @since 2.0
          */
        }
      else
        stream << PRIVATE
      end
      stream << 'typedef enum {'
      i = 0; stream << (variants.collect { |name, type| "#{identifier(name)} = #{i+=1}" }).join(',')
      stream << "} #{tag_};"
      if public?
        stream << %{
          /**
            #{ingroup}

            @brief Opaque struct holding state of the box

            @since 2.0
          */
        }
      else
        stream << PRIVATE
      end
      stream << 'typedef struct {union {'
        variants.each { |name, type| stream << field_declaration(type, name) }
      stream << "} variant; /**< @private */ #{tag_} tag; /**< @private */} #{signature};"
    end

    def type_tag = "#{signature}<#{variants.values.join(',')}>"

  private

    # @private
    def setup_variants(variants)
      @variants = variants.transform_values { |type| type.to_type }
      self.variants.each_value { |type| dependencies << type }
    end

    # @private
    def field_variable(opt)
      if opt.is_a?(::Hash)
        obj, name = opt.first
        "#{obj}->#{name}"
      else
        opt
      end
    end

    # @private
    def field_declaration(type, name)
      s = "#{type} #{field_variable(name)};"
      s += '/**< @private */' if @opaque
      s
    end

    def configure
      super
      default_create.configure do
        inline_code %{
          assert(target);
          target->tag = (#{tag_})0;
        }
      end
      method(tag_, :tag, { target: const_rvalue }).configure do
        inline_code %{
          assert(target);
          return target->tag;
        }
        header %{
          @brief Get type of contained element

          @param[in] target box to query
          @return tag of currently contained element

          This function returns a tag of currently contained element (@ref #{tag_}) or zero value if the box is empty (i.e. contains nothing).

          @since 2.0
        }
      end
      method(:void, :purge, { target: rvalue }).configure do
        code %{
          assert(target);
          #{destroy.(target) if destructible?};
          #{default_create.(target)};
        }
        header %{
          @brief Reset box

          @param[in] target box to purge

          This function resets the box by destroying containing element (if any).
          The box is left empty.

          @since 2.0
        }
      end
      ### destroy
        _code = %$
          assert(target);
          if(target->tag) switch(target->tag) {
        $
        variants.each do |name, type|
          _code += "case #{identifier(name)}:"
          _code += type.destroy.("target->variant.#{name}") if type.destructible?
          _code += ';break;'
        end
        _code += %{
          default: #{@default}
        }
        _code += '}'
        destroy.configure { code _code }
      ### copy
        _code = %$
          assert(target);
          assert(source);
          if(source->tag) switch(source->tag) {
        $
        variants.each do |name, type|
          _code += "case #{identifier(name)}:"
          _code += type.copy.("target->variant.#{name}", "source->variant.#{name}")
          _code += ';break;'
        end
        _code += %{
          default: #{@default}
        }
        _code += '} target->tag = source->tag;'
        copy.configure { code _code }
      ### equal
        _code = %$
          assert(left);
          assert(right);
          if(left->tag != right->tag) return 0;
          if(!left->tag) return 1;
          switch(left->tag) {
        $
        variants.each do |name, type|
          _code += "case #{identifier(name)}:"
          _code += 'return '
          _code += type.equal.("left->variant.#{name}", "right->variant.#{name}")
          _code += ';'
        end
        _code += %{
          default: #{@default}
        }
        _code += '}'
        equal.configure { code _code }
      ### compare
        _code = %$
          assert(left);
          assert(right);
          assert(left->tag == right->tag);
          if(!left->tag) return 0;
          switch(left->tag) {
        $
        variants.each do |name, type|
          _code += "case #{identifier(name)}: return "
          _code += type.compare.("left->variant.#{name}", "right->variant.#{name}")
          _code += ';'
        end
        _code += %{
          default: #{@default}
        }
        _code += '}'
        compare.configure { code _code }
      ### hash_code
        _code = %$
          assert(target);
          if(!target->tag) return AUTOC_HASHER_SEED;
          switch(target->tag) {
        $
        variants.each do |name, type|
          _code += "case #{identifier(name)}: return "
          _code += type.hash_code.("target->variant.#{name}")
          _code += ';'
        end
        _code += %{
          default: #{@default}
        }
        _code += '}'
        hash_code.configure { code _code }
      ### typed accessors
      _type = self
      variants.each do |name, type|
        method(type.const_lvalue, "view_#{name}", { target: const_rvalue }).configure do
          code %{
            assert(target);
            assert(target->tag == #{identifier(name)});
            return &target->variant.#{name};
          }
          header %{
            @brief Get a view of contained value of type #{type}

            @param[in] target box to query
            @return a view of the value of type #{type}

            This function is used to get a constant reference (in form of the C pointer) to value contained in `target`.

            It is the caller's responsibility to check the type of currently contained value.
            Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

            @see @ref #{tag}

            @since 2.0
          }
        end
        if type.copyable?
          method(type, "get_#{name}", { target: const_rvalue }).configure do
            code %{
              #{type} result;
              assert(target);
              assert(target->tag == #{identifier(name)});
              #{type.copy.(:result, "target->variant.#{name}")};
              return result;
            }
            header %{
              @brief Get a copy of contained value of type #{type}
  
              @param[in] target box to query
              @return a copy of the value of type #{type}
  
              This function is used to get a copy of the value contained in `target`.
  
              It is the caller's responsibility to check the type of currently contained value.
              Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`
  
              @see @ref #{tag}
  
              @since 2.0
            }
          end
          method(:void, "put_#{name}", { target: rvalue, value: type.const_rvalue }).configure do
            code %{
              assert(target);
              #{destroy.(target) if destructible?};
              #{type.copy.("target->variant.#{name}", value)};
              target->tag = #{identifier(name)};
            }
            header %{
              @brief Put value of type #{type} into box
  
              @param[in] target box to set
              @param[in] value value of type #{type} to put
  
              This function is used to put a copy of the value into the box.
              Previously contained value is destroyed with respective destructor.
  
              After call to this function the value type may be obtained with @ref #{identifier(:tag)}.

              @see @ref #{tag}
  
              @since 2.0
            }
          end
        end
      end
    end

  end # Box


end
orderable?(= variants.values.all? { |t| t.orderable? }) click to toggle source
# File lib/autoc/box.rb, line 25
  def orderable? = variants.values.all? { |t| t.orderable? }
  def copyable? = variants.values.all? { |t| t.copyable? }
  def hashable? = variants.values.all? { |t| t.hashable? }

  def initialize(type, variants, visibility: :public)
    super(type, visibility:)
    setup_variants(variants)
    @tag_ = "#{signature}_";
    @default = 'abort();'
  end

  def render_interface(stream)
    if public?
      stream << %{
        /**
          #{defgroup}
          @brief Value type wrapper of the C union
        */
      }
      stream << %{
        /**
          #{ingroup}

          @brief Box tag set

          Use @ref #{identifier(:tag)} to query current box contents. Empty box is always tagged as 0.

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef enum {'
    i = 0; stream << (variants.collect { |name, type| "#{identifier(name)} = #{i+=1}" }).join(',')
    stream << "} #{tag_};"
    if public?
      stream << %{
        /**
          #{ingroup}

          @brief Opaque struct holding state of the box

          @since 2.0
        */
      }
    else
      stream << PRIVATE
    end
    stream << 'typedef struct {union {'
      variants.each { |name, type| stream << field_declaration(type, name) }
    stream << "} variant; /**< @private */ #{tag_} tag; /**< @private */} #{signature};"
  end

  def type_tag = "#{signature}<#{variants.values.join(',')}>"

private

  # @private
  def setup_variants(variants)
    @variants = variants.transform_values { |type| type.to_type }
    self.variants.each_value { |type| dependencies << type }
  end

  # @private
  def field_variable(opt)
    if opt.is_a?(::Hash)
      obj, name = opt.first
      "#{obj}->#{name}"
    else
      opt
    end
  end

  # @private
  def field_declaration(type, name)
    s = "#{type} #{field_variable(name)};"
    s += '/**< @private */' if @opaque
    s
  end

  def configure
    super
    default_create.configure do
      inline_code %{
        assert(target);
        target->tag = (#{tag_})0;
      }
    end
    method(tag_, :tag, { target: const_rvalue }).configure do
      inline_code %{
        assert(target);
        return target->tag;
      }
      header %{
        @brief Get type of contained element

        @param[in] target box to query
        @return tag of currently contained element

        This function returns a tag of currently contained element (@ref #{tag_}) or zero value if the box is empty (i.e. contains nothing).

        @since 2.0
      }
    end
    method(:void, :purge, { target: rvalue }).configure do
      code %{
        assert(target);
        #{destroy.(target) if destructible?};
        #{default_create.(target)};
      }
      header %{
        @brief Reset box

        @param[in] target box to purge

        This function resets the box by destroying containing element (if any).
        The box is left empty.

        @since 2.0
      }
    end
    ### destroy
      _code = %$
        assert(target);
        if(target->tag) switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.destroy.("target->variant.#{name}") if type.destructible?
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      destroy.configure { code _code }
    ### copy
      _code = %$
        assert(target);
        assert(source);
        if(source->tag) switch(source->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.copy.("target->variant.#{name}", "source->variant.#{name}")
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '} target->tag = source->tag;'
      copy.configure { code _code }
    ### equal
      _code = %$
        assert(left);
        assert(right);
        if(left->tag != right->tag) return 0;
        if(!left->tag) return 1;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += 'return '
        _code += type.equal.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      equal.configure { code _code }
    ### compare
      _code = %$
        assert(left);
        assert(right);
        assert(left->tag == right->tag);
        if(!left->tag) return 0;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.compare.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      compare.configure { code _code }
    ### hash_code
      _code = %$
        assert(target);
        if(!target->tag) return AUTOC_HASHER_SEED;
        switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.hash_code.("target->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      hash_code.configure { code _code }
    ### typed accessors
    _type = self
    variants.each do |name, type|
      method(type.const_lvalue, "view_#{name}", { target: const_rvalue }).configure do
        code %{
          assert(target);
          assert(target->tag == #{identifier(name)});
          return &target->variant.#{name};
        }
        header %{
          @brief Get a view of contained value of type #{type}

          @param[in] target box to query
          @return a view of the value of type #{type}

          This function is used to get a constant reference (in form of the C pointer) to value contained in `target`.

          It is the caller's responsibility to check the type of currently contained value.
          Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

          @see @ref #{tag}

          @since 2.0
        }
      end
      if type.copyable?
        method(type, "get_#{name}", { target: const_rvalue }).configure do
          code %{
            #{type} result;
            assert(target);
            assert(target->tag == #{identifier(name)});
            #{type.copy.(:result, "target->variant.#{name}")};
            return result;
          }
          header %{
            @brief Get a copy of contained value of type #{type}

            @param[in] target box to query
            @return a copy of the value of type #{type}

            This function is used to get a copy of the value contained in `target`.

            It is the caller's responsibility to check the type of currently contained value.
            Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

            @see @ref #{tag}

            @since 2.0
          }
        end
        method(:void, "put_#{name}", { target: rvalue, value: type.const_rvalue }).configure do
          code %{
            assert(target);
            #{destroy.(target) if destructible?};
            #{type.copy.("target->variant.#{name}", value)};
            target->tag = #{identifier(name)};
          }
          header %{
            @brief Put value of type #{type} into box

            @param[in] target box to set
            @param[in] value value of type #{type} to put

            This function is used to put a copy of the value into the box.
            Previously contained value is destroyed with respective destructor.

            After call to this function the value type may be obtained with @ref #{identifier(:tag)}.

            @see @ref #{tag}

            @since 2.0
          }
        end
      end
    end
  end

end 
render_interface(stream) click to toggle source
# File lib/autoc/box.rb, line 36
def render_interface(stream)
  if public?
    stream << %{
      /**
        #{defgroup}
        @brief Value type wrapper of the C union
      */
    }
    stream << %{
      /**
        #{ingroup}

        @brief Box tag set

        Use @ref #{identifier(:tag)} to query current box contents. Empty box is always tagged as 0.

        @since 2.0
      */
    }
  else
    stream << PRIVATE
  end
  stream << 'typedef enum {'
  i = 0; stream << (variants.collect { |name, type| "#{identifier(name)} = #{i+=1}" }).join(',')
  stream << "} #{tag_};"
  if public?
    stream << %{
      /**
        #{ingroup}

        @brief Opaque struct holding state of the box

        @since 2.0
      */
    }
  else
    stream << PRIVATE
  end
  stream << 'typedef struct {union {'
    variants.each { |name, type| stream << field_declaration(type, name) }
  stream << "} variant; /**< @private */ #{tag_} tag; /**< @private */} #{signature};"
end
setup_variants(variants) click to toggle source

@private

# File lib/autoc/box.rb, line 84
def setup_variants(variants)
  @variants = variants.transform_values { |type| type.to_type }
  self.variants.each_value { |type| dependencies << type }
end
type_tag(= " click to toggle source
# File lib/autoc/box.rb, line 79
  def type_tag = "#{signature}<#{variants.values.join(',')}>"

private

  # @private
  def setup_variants(variants)
    @variants = variants.transform_values { |type| type.to_type }
    self.variants.each_value { |type| dependencies << type }
  end

  # @private
  def field_variable(opt)
    if opt.is_a?(::Hash)
      obj, name = opt.first
      "#{obj}->#{name}"
    else
      opt
    end
  end

  # @private
  def field_declaration(type, name)
    s = "#{type} #{field_variable(name)};"
    s += '/**< @private */' if @opaque
    s
  end

  def configure
    super
    default_create.configure do
      inline_code %{
        assert(target);
        target->tag = (#{tag_})0;
      }
    end
    method(tag_, :tag, { target: const_rvalue }).configure do
      inline_code %{
        assert(target);
        return target->tag;
      }
      header %{
        @brief Get type of contained element

        @param[in] target box to query
        @return tag of currently contained element

        This function returns a tag of currently contained element (@ref #{tag_}) or zero value if the box is empty (i.e. contains nothing).

        @since 2.0
      }
    end
    method(:void, :purge, { target: rvalue }).configure do
      code %{
        assert(target);
        #{destroy.(target) if destructible?};
        #{default_create.(target)};
      }
      header %{
        @brief Reset box

        @param[in] target box to purge

        This function resets the box by destroying containing element (if any).
        The box is left empty.

        @since 2.0
      }
    end
    ### destroy
      _code = %$
        assert(target);
        if(target->tag) switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.destroy.("target->variant.#{name}") if type.destructible?
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      destroy.configure { code _code }
    ### copy
      _code = %$
        assert(target);
        assert(source);
        if(source->tag) switch(source->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += type.copy.("target->variant.#{name}", "source->variant.#{name}")
        _code += ';break;'
      end
      _code += %{
        default: #{@default}
      }
      _code += '} target->tag = source->tag;'
      copy.configure { code _code }
    ### equal
      _code = %$
        assert(left);
        assert(right);
        if(left->tag != right->tag) return 0;
        if(!left->tag) return 1;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}:"
        _code += 'return '
        _code += type.equal.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      equal.configure { code _code }
    ### compare
      _code = %$
        assert(left);
        assert(right);
        assert(left->tag == right->tag);
        if(!left->tag) return 0;
        switch(left->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.compare.("left->variant.#{name}", "right->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      compare.configure { code _code }
    ### hash_code
      _code = %$
        assert(target);
        if(!target->tag) return AUTOC_HASHER_SEED;
        switch(target->tag) {
      $
      variants.each do |name, type|
        _code += "case #{identifier(name)}: return "
        _code += type.hash_code.("target->variant.#{name}")
        _code += ';'
      end
      _code += %{
        default: #{@default}
      }
      _code += '}'
      hash_code.configure { code _code }
    ### typed accessors
    _type = self
    variants.each do |name, type|
      method(type.const_lvalue, "view_#{name}", { target: const_rvalue }).configure do
        code %{
          assert(target);
          assert(target->tag == #{identifier(name)});
          return &target->variant.#{name};
        }
        header %{
          @brief Get a view of contained value of type #{type}

          @param[in] target box to query
          @return a view of the value of type #{type}

          This function is used to get a constant reference (in form of the C pointer) to value contained in `target`.

          It is the caller's responsibility to check the type of currently contained value.
          Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

          @see @ref #{tag}

          @since 2.0
        }
      end
      if type.copyable?
        method(type, "get_#{name}", { target: const_rvalue }).configure do
          code %{
            #{type} result;
            assert(target);
            assert(target->tag == #{identifier(name)});
            #{type.copy.(:result, "target->variant.#{name}")};
            return result;
          }
          header %{
            @brief Get a copy of contained value of type #{type}

            @param[in] target box to query
            @return a copy of the value of type #{type}

            This function is used to get a copy of the value contained in `target`.

            It is the caller's responsibility to check the type of currently contained value.
            Consider using guard `if(#{identifier(:tag)}(...) == #{identifier(name)}) ...`

            @see @ref #{tag}

            @since 2.0
          }
        end
        method(:void, "put_#{name}", { target: rvalue, value: type.const_rvalue }).configure do
          code %{
            assert(target);
            #{destroy.(target) if destructible?};
            #{type.copy.("target->variant.#{name}", value)};
            target->tag = #{identifier(name)};
          }
          header %{
            @brief Put value of type #{type} into box

            @param[in] target box to set
            @param[in] value value of type #{type} to put

            This function is used to put a copy of the value into the box.
            Previously contained value is destroyed with respective destructor.

            After call to this function the value type may be obtained with @ref #{identifier(:tag)}.

            @see @ref #{tag}

            @since 2.0
          }
        end
      end
    end
  end

end