Library name: Opaque

Author: Calum Grant

Description

A C++ container to store opaque data. An opaque container is like a black box: you can store data inside it, copy it around safely (for any data type), and extract the data. Its main benefit is that it stores data in an internal buffer, so can be used to avoid heap allocation.

This class is a more generic Boost.Variant, or Boost.Optional.  The only constraint on the stored value is that it fits in the buffer (this is checked at compile-time), and that it has a public copy-constructor.

Another use is for an efficient factory.  A factory usually involves dynamic memory allocation since the exact type of the returned object is unknown.  Using an opaque container, the factory can create the object in opaque data, thereby saving a memory allocation.

Download

Copyright: Calum Grant

License: Boost Software License 1.0

Download: opaque-20060422.zip

opaque

Purpose: An opaque datatype. 

Description: Will hold, and copy by value, any C++ datatype, provided that it fits in its internal buffer.  The size of the buffer can be controlled by a template argument (number of doubles), the default size being 4 doubles (32 bytes).  A compile-time error will result if the object is too large for the buffer.  The only other constraint on the datatype is that it has a public copy constructor.

Even though the type of the object is unknown, the object can still be copied and destroyed safely.

Synopsis:

	template<int Size> class opaque_t;
	typedef opaque_t<4> opaque;

Examples:

#include "opaque.hpp"
#include "opaque_functor.hpp"

#include <cassert>
#include <string>
#include <iostream>

using cg::opaque;
using cg::opaque_t;
using cg::opaque_ptr;
	
void opaque_test()
{
	// Default constructor creates an empty object
	opaque a;
	assert(a.empty());

	// operator bool tests for empty
	assert(!a);

	// Pass any data type into the constructor to store it
	// (provided that it fits)
	opaque b=1, c=(char*)"Hello 1";

	// Now, b and c are not empty
	assert(!b.empty());
	assert(b);

	// We can test the type using the test() method
	assert(b.test<int>());
	assert(c.test<char*>());

	// The test will fail for other types
	assert(!b.test<double>());
	assert(!c.test<char>());

	// We can fetch the value from the opaque type
	assert(b.get<int>() == 1);

	// We can store non-PODs
	opaque d = std::string("Hello 2");

	// We can copy values in the copy constructor
	opaque e=d;

	// We can choose/change the size of the opaque, if we assign to a larger one
	// (assigning to a smaller opaque might result in buffer overrun).
	opaque_t<2> f = 3.0;
	opaque_t<3> g=f;
	c=g;
	assert(c.get<double>() == 3.0);
	assert(g.get<double>() == 3.0);

	// We can assign in between opaque types
	d = d;	// No-op
	d = f;
	a = e;
	assert(d.test<double>());
	assert(a.get<std::string>() == "Hello 2");

	// We can reassign an opaque to any other type
	a = 3.0f;
	assert(a.test<float>());
	assert(a.get<float>() == 3.0f);

	// We can clear an opaque
	a.clear();
	assert(a.empty());
	
	// We can access the type_info of the stored object
	assert(b.type() == typeid(int));
	assert(b.test<int>());
}

opaque_ptr<T>

Purpose: Stores a value of type T, or a subtype of T, or is empty.

Description: This behaves like a smart pointer, which stores its data internally.  It has * and -> operators to access the data.  opaque_ptr  only holds objects of type T, or subtypes of T,   The advantage of this container is that it avoids a memory allocation, so can be used for an efficient factory or efficient polymorphism.

Synopsis:

	template<typename T, int Size=4> class opaque_ptr : public opaque_t<Size>

Examples:

struct F
{
	virtual std::string data() const { return "F"; }
};

struct G : public F
{
	virtual std::string data() const { return "G"; }
};

struct H : public F
{
	virtual std::string data() const { return "H"; }
};

void factory(cg::opaque_ptr<F> &op)
{
	op = G();
}

void opaque_ptr_test()
{
	// An opaque_ptr is a smart pointer: it is either "null" or points
	// to something of a particular type.

	// Initially, an opaque_ptr is empty
	opaque_ptr<F> a, b=a;
	assert(!a);
	assert(!b);

	// We can call a factory to create an object of the particular type
	factory(a);
	assert(a);
	assert(a->data() == "G");

	// Opaque objects are copied by value:
	opaque_ptr<F> c=a, d;
	assert(c->data() == "G");
	d=c;
	assert(d->data() == "G");

	// We can specify the size of the opaque object
	opaque_ptr<F,1> e;

	// We can assign a small object to a larger one:
	a=e;

	// But we cannot assign a large object to a smaller one:
	// e=a;  // compile-time error

	// We can convert between different types of pointer
	opaque_ptr<H> f = H();

	a=f;
	assert(a->data() == "H");

	// We cannot downcast:
	// f=a;  // compile-time error
}