Clone wiki

clReflect / Containers


WARNING: The Container implementation in clReflect is in constant development and may be redesigned in the near future. This document is valid as of 30th April, 2012. Ideas on how to improve it are always welcome!


There are two types of array native to C++:

struct Test
   // Pointer to a block of memory that may or may not be an array
   int* ptr_array;

   // Old-style C array
   int c_array[3];

Both of these are reflected by clReflect and stored in the runtime database:

// Your loaded database and field names
clcpp::Database db;
const unsigned int ptr_array_hash = db.GetName("ptr_array").hash;
const unsigned int c_array_hash = db.GetName("c_array").hash;

// Get access to the field descriptions for the arrays
const clcpp::Type* test_type = clcpp::GetType<Test>();
const clcpp::Field* ptr_array = FindPrimitive(test_type->fields, ptr_array_hash);
const clcpp::Field* c_array = FindPrimitive(test_type->fields, c_array_hash);

The ptr_array field can be a pointer to one or many objects; behaviour determined at runtime and unavailable at compile-time. Given a Test object, access is quite simple:

// Get a pointer to the field in the object
Test test;
int** ptr_array_ptr = (int**)((char*)&test + ptr_array->offset);

// Point to a local variable
int x;
*ptr_array_ptr = &x;

c_array can be accessed in exactly the same manner, however because its array size is known at compile-time, the clRefect database stores more information. It may help to have the definition of Field open in your browser as you read this. If the ci pointer in Field is non-null, the field describes a C Array. This points to an object of type ContainerInfo that describes C arrays and other custom containers (covered later).

Immediately, at runtime, you can query the number of elements in the array:

int count = c_array->ci->count;

Using this information you can read and write to the array while knowing its type. However, there is a small library of tools that can do this for you, abstracting the array to the level of container. These reside in the Containers.h header file.

The types in this header file allow you to iterate and modify any container, allowing you to create generalised container access functions. Reading is achieved using the ReadIterator type:

// This uses the field-specific constructor of ReadIterator that only works with C arrays
clcpp::ReadIterator reader(c_array, (char*)&test + c_array->offset);
for (unsigned int i = 0; i < reader.m_Count; i++)
   // Assume there is no key and the value points to an integer
   clcpp::ContainerKeyValue kv = reader.GetKeyValue();
   printf("%d ", *(int*)kv.value);

To allow more general code, the Iterator type specifies full type information for the key and value.

The value pointer points directly to the actual object within the container and is not a copy. This allows you to walk over the container and modify its contents. As you can't add or remove elements from a C array, this is all the information you should need to work with them.

For more complicated containers, you will require use of the WriteIterator type to add and remove elements.

Custom Containers

You will no doubt have custom arrays, linked lists, maps and other containers in your projects. clReflect allows you to register these as containers and anonymously gain access to them at runtime, transparently allowing use by serialisation, network communication, pointer graph walking, or whatever use you have for the containers.

This is where the API gets a bit rough, in need of a bit of work to make it simpler and more powerful. Let's start with a basic, reflected vector type (partially implemented):

// As clang doesn't support attributing of templates, you must use this macro
// to reflect templates. Ensure it's introduced in the global namespace.

// Simple base for all your containers, exposing data necessary for clRelect
struct Container
   void* data;
   int count;

template <typename TYPE>
class Vector : public Container
   // Typical modification functions
   void Add(const TYPE& value);
   TYPE& GetAt(int index) { return m_TypedData[index]; }

   TYPE* m_TypedData;

The first job is to write a read iterator for your container:

class VectorReadIterator : public clcpp::IReadIterator
      : m_Container(0)
      , m_ElementSize(0)
      , m_Position(0)
      , m_Count(0)

   // Implementation of the read iterator interface

   void Initialise(const clcpp::Primitive* primitive, const void* container_object, clcpp::ReadIterator& storage)
      // Custom containers can only be attached to template types
      // This check ensures the primitive matches that requirement
      assert(primitive->kind == clcpp::Primitive::KIND_TEMPLATE_TYPE);
      const clcpp::TemplateType* template_type = (clcpp::TemplateType*)primitive;

      // Describe the vector value type
      m_Container = (Container*)container_object;
      storage.m_ValueType = template_type->parameter_types[0];
      storage.m_ValueIsPtr = template_type->parameter_ptrs[0];

      // The value type may not be reflected
      if (storage.m_ValueType != 0)
            // Determine the size of an element
            m_ElementSize = storage.m_ValueType->size;
            if (storage.m_ValueIsPtr)
                  m_ElementSize = sizeof(void*);

		// Calculate how many elements are in the vector
		storage.m_Count = container.count / m_ElementSize;
		m_Count = storage.m_Count;

   clcpp::ContainerKeyValue GetKeyValue() const
      assert(m_Position < m_Count);
      clcpp::ContainerKeyValue kv;
      kv.value = (char*) + m_Position * m_ElementSize;

      return kv;

   void MoveNext()

   // Construction values
   const Container* m_Container;
   unsigned int m_ElementSize;

   // Current iteration position
   unsigned int m_Position;
   unsigned int m_Count;

You then implement a write iterator:

class VectorWriteIterator : public clcpp::IWriteIterator


   // Implementation of the IWriteIterator interface
   void Initialise(const clcpp::Primitive* primitive, void* container_object, clcpp::WriteIterator& storage, int count)


Finally, you notify clReflect of your custom container by specifying this in the global namespace:

clcpp_container_iterators(Vector, VectorReadIterator, VectorWriteIterator, nokey)