ifndef Rice__ruby_struct__hpp_ define Rice__ruby_struct__hpp_

include “Array.hpp” include “Hash.hpp” include “Module.hpp” include “Class.hpp” include “Builtin_Object.hpp” include “Address_Registration_Guard.hpp”

namespace Rice {

class Struct;

//! Define a new Struct Struct define_struct();

//! A wrapper for creating Struct classes.

! The Struct class is used for creating new Classes.  Note that the
  notation used here differs slightly from the notation inside the
  interpreter.

  Inside the interpreter, calling Struct.new creates a new Class:
  \code
  irb(main):001:0> MyStruct = Struct.new(:a, :b, :c)
  => S
  irb(main):002:0> MyStruct.class
  => Class
  \endcode

  Instantiating that Class creates an instance of that Class:
  \code
  irb(main):003:0> mystruct_instance = MyStruct.new
  => #<struct MyStruct a=nil, b=nil, c=nil>
  irb(main):004:0> mystruct_instance.class
  => MyStruct
  irb(main):005:0> mystruct_instance.class.ancestors
  => [MyStruct, Struct, Enumerable, Object, Kernel]
  \endcode

  Thus, inside the interpreter, MyStruct is a Class which inherits
  from Struct, and mystruct_instance is an instance of MyStruct.

  At the C++ level, we might do this instead:
  \code
  Struct rb_cMyStruct = define_struct()
    .define_member("a")
    .define_member("b")
    .define_member("c")
    .initialize("MyStruct");
  Struct::Instance mystruct_instance(rb_cMyStruct.new_instance());
  \endcode

 Here rb_cMyStruct is an instance of Struct and that mystruct_instance
 is an instance of Struct::Instance.

class Struct

: public Module_impl<Class, Struct>

{ public:

//! Create a new Struct.
Struct();

//! Copy constructor.
Struct(Struct const & s);

//! Destructor.
virtual ~Struct();

//! Define a new Struct member.
/*! Defines a new member of the Struct.  Must be called before the
    Struct is initialized.
    \return *this
  /
Struct & define_member(
    Identifier name);

//! Initialize the Struct class.
/*! Must be called after all Struct members have been defined.
    \param module the module under which to define the Struct.
    \param name the name of the Class at the ruby level.
  /
Struct & initialize(
    Module module,
    Identifier name);

//! Get the offset of a member in the Struct.
/*! Internally, Struct members are stored as a single array of VALUE.
    This function determines the offset of a given member in that
    array.
    \param member the name of the desired member.
    \return the index of the given member.
  /
size_t offset_of(Identifier name) const;

class Instance;
friend class Instance;
friend Struct Rice::define_struct();

//! Create a new instance of the Struct
/*! \param args the arguments to the constructor.
    \return a new Struct::Instance
  /
Instance new_instance(Array args = Array()) const;

//! Swap with another Struct.
void swap(Struct & other);

Array members() const { return members_; }

private:

Array members_;
Address_Registration_Guard members_guard_;

Hash member_offset_;
Address_Registration_Guard member_offset_guard_;

};

//! An instance of a Struct //! sa Struct class Struct::Instance

: public Builtin_Object<T_STRUCT>

{ public:

//! Create a new Instance of a Struct.
/*! \param type the Struct type to create.
    \param args the initial values for the objects of the instance.
  /
Instance(
    Struct const & type,
    Array args = Array());

//! Encapsulate an existing Struct instance.
/*! \param type the Struct type to encapsulate.
    \param s the instance to encapsulate.
  /
Instance(
    Struct const & type,
    Object s);

//! Get a member, given its offset.
/*! \param index the (integral) index into the Struct's internal
    array or its name (an Identifier or char const *)
    \return the member.
  /
template<typename T>
Object operator[](T index);

//! Swap with another Struct::Instance.
void swap(Instance & other);

private:

Struct type_;

};

} // namespace Rice

include “Struct.ipp”

endif // Rice__ruby_struct__hpp_