Dedicating a field in arbitrary class types for "external use"
My conta开发者_如何学编程iner 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 classProvidesExternalUseField
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.
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.
精彩评论