10

I am writing a C++ wrapper around a legacy API. This API provides me with a pointer value to keep extra data, and I want to implement the Small Buffer Optimization with it.

I have implemented an is_small_pod metafunction that checks whether a given type is POD and it fits within a void*:

template< typename Type >
struct is_small_pod
  : std::integral_constant<
        bool
      , std::is_pod< Type >::type::value
        && sizeof( Type ) <= sizeof( void* )
    >
{};

and I'm setting the value like this:

// void*& param;
if( detail::is_small_pod< Type >() )
{
    *static_cast< Type* >( &param ) = value;
} else {
    param = new Type( value );
}

Am I implementing this optimization correctly? I believe this will fail when the value alignment is not compatible with the alignment of a pointer (odd corner case as it may be). Is that situation even possible, or am I just overthinking it? How should I extend my metafunction to check for compatible alignment as well?

K-ballo
  • 80,396
  • 20
  • 159
  • 169

2 Answers2

8

It is not possible for the alignment of a type to be greater than the size of that type.

3.11 Alignment [basic.align]

[...] An alignment is an implementation-defined integer value representing the number of bytes between successive addresses at which a given object can be allocated.

5.3.3 Sizeof [expr.sizeof]

2 - [...] the size of an array of n elements is n times the size of an element.

So, your code can only break if alignof(void *) < sizeof(void *), which is not the case on most platforms.

For safety, you can write:

template< typename Type >
struct is_small_pod
  : std::integral_constant<
        bool
      , std::is_pod< Type >::type::value
        && sizeof( Type ) <= sizeof( void* )
        && alignof( Type ) <= alignof( void* )
    >
{};
Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • While technically correct, this does ignore everyday things like 16-byte alignment requirements for SSE* support. – ildjarn Oct 26 '12 at 05:35
  • @ildjarn for SSE you generally tend to use `__m128` etc., which is 16 bytes in size and in alignment. – ecatmur Oct 26 '12 at 07:31
0

Just as a general approach, you can always try testing your theories. I imagine you would do something like this:

template< class Type >
bool TestAlignmentSanity( Type value )
{
    // This function is only valid for small POD types
    if( !detail::is_small_pod< Type >() )
        return false;

    // Temporary space covering alignments spanning the size of a void*
    const int nBytes = sizeof(void*);
    char buffer[sizeof(Type) + nBytes - 1];

    // For each target alignment, test that a copy is successful.
    for( int i = 0; i < nBytes; i++ )
    {
       Type * target = static_cast< Type* >( &buffer[i] );

       // Sanity-check that the pointer was actually aligned as we requested
       if( (char*)target != &buffer[i] ) return false;

       // Copy and test that the result is as expected.  Assumes '==' operator
       // is defined...  Otherwise, implement with byte comparisons.
       *target = value;
       if( !(*target == value) ) return false;
    }

    return true;
}

Here I tested that the data type can be copied into any alignment that spans the size of a void*. It's just a thought. =)

paddy
  • 60,864
  • 6
  • 61
  • 103