Dedicating a field in arbitrary class types for "external use"

181 Views Asked by At

My container needs to store a little information about its elements. Normally, I store this separately from elements. However, I'd like to give users possibility to conserve memory by dedicating a field in element structure type for external use. E.g.:

struct MyStuff
{
  int           foo;
  char          bar;
  mutable char  dedicated_for_external_use;  // Because of alignment, this field
                                             // won't increase sizeof (MyStuff)
};

The idea here is that the field must not be accessed by anything but element's container. Since containers store a copy (much like std::vector), it wouldn't be a problem if you added any given value x to several containers.

How would you design an interface for this that, if possible, would meet the following requirements?

  • Should be completely optional. I.e. it should be possible to automatically determine if given type provides such a field or not and then container would only use it if available.
  • Ideally, wouldn't depend on type traits etc. as I need maximum compiler compatibility.
  • Should be easy to use. I.e. if you can and want to enable this optimization for type MyStuff, you could do it with 3 lines of code, not 25. Internal complications, on the other hand, don't matter.
  • Should preferably exclude false positives completely. What I mean is: if you check for field foo_bar there is a small posibility that such field exists for a completely unrelated reason (and I think duck-typing is simply not for C++). A better way would be to check if type inherits marker class ProvidesExternalUseField from my library, as this can't be by accident.

EDIT

I know about Boost.Intrusive, but what I want is something different. If I go that way and create a hooks class with a single char field, it cannot be used to conserve memory in many cases. If inherited type has an int as first field, char field will be padded to 4 bytes. I.e. you'd often need intricate knowledge of type internals to be able to "squeeze" such extern-use field in, but inheritance doesn't really provide it:

struct hooks { mutable char dedicated_for_external_use; };
struct MyStuff : hooks
{
  int           foo;
  char          bar;
};

Here, size of MyStuff will be 12 bytes, not 8.

1

There are 1 best solutions below

0
On

You can use partial template specialization for the case your data struct derives from the marker interface.

Let's say your marker interface class looks like this:

class ProvidesExternalUseField
{
public:
    char GetExtraField () { return 0; }
    void SetExtraField (char newVal) {}
};

It is not virtual for a purpose: we wouldn't want to add a vtable pointer to a data class just for this.

Now let's implement a simple container class:

template <class T>
class Container
{
public:
    char GetExtraValue ()
    {
        return 0; // here we cannot know if T is derived from the marker
    }
private:
    T m_t;
};

And here is how we change it to distinguish between the 2 cases:

template <class T, bool DoesTProvideExternalUseField>
class ContainerImpl
{
public:
    char GetExtraValue () { return 0; }

private:
    T m_t;
};

template <class T>
class ContainerImpl<T, true>
{
public:
    char GetExtraValue () { return m_t.GetExtraField(); } 
private:
    T m_t;
};

template <class T>
class Container: public ContainerImpl<T,
                                      boost::is_base_of<ProvidesExternalUseField,T>::value>
{
};

Now you can define the structs like this:

struct A
{
    int m_intVal;
};

struct B: public ProvidesExternalUseField
{
    char GetExtraField () { return m_extraField; }
    void SetExtraField (char newVal) { m_extraField = newVal; }

    int m_intVal;
    char m_charVal;
    char m_extraField;
};

And use the container class in the exact same way:

Container<A> a;
Container<B> b;

You can also further automate (templatize) getters and setters in the marker interface by using a poiter-to-member as a template parameter.