Boost logo

Boost :

Subject: Re: [boost] How to get the size of an C-array?
From: Andrey Semashev (andrey.semashev_at_[hidden])
Date: 2009-10-15 13:40:28


vicente.botet wrote:

>> There is no portable way to acquire the dynamic array length. IMO,
>> there are two practical solutions for this:
>>
>> 1. Put everything you need into T's destructor. If T is a third
>> party type then a wrapper class around T can serve the purpose.
>
> Well, the problem is that we need to do something before the
> destructor is called. When deleting an array of transactional objects
> we need to mark each one of the elements as if they were written, so
> the conclict detection cat detect other threads been written on a
> specific array element.

I'm not sure I understand you here. Are you saying that other threads
may be accessing the array elements during the destruction?

>> Regarding the trick with the prepended length storing, it can solve
>> the problem, but it introduces pointer magic which may not be
>> obvious for the ones reading the code (including yourself a couple
>> months later). And, AFAIK, you won't be able to override the
>> standard new and delete operators to behave that way, so you'll
>> have to use dedicated functions to allocate/deallocate such arrays.
>> Therefore I wouldn't recommend it unless there are significant
>> reasons for it.
>
> No, I don't want to overload the standard new and delete operators,
> just provide a mixing helping the user to define transactional
> objects. For these transactional objects, the overload of the
> operator new and new[] could set some information that will allow us
> to define a free function size taking a transactional object pointer
> and returning its size.

I think moving a bit closer to your use-case might help. Am I correct if
I say that the type of array elements is provided by your library and
this size acquisition is only needed for such arrays? If so then yes,
you can override new and delete (and new[] and delete[]) for your
classes to store additional info in the allocated memory.

> The problem I find overloading new[] is that I dont know where my
> prefixed information will be placed respect to the returned pointer,
> as the standard, prefix it already to store its own specific
> information.

The pointer returned by new[] defined for your class will be used to
construct array elements. Thus your implementation should allocate
memory of size = sizeof(info) + possible alignment gap +
sizeof(elements), where the last argument is passed to your operator
new[], and return the pointer to the beginning of the elements storage
area (i.e. behind the info and alignment gap).

The way you allocate memory is not relevant, you may use malloc or
global operator new[] or something else. The additional information that
the underlying allocator may or may not store before the pointer it
returns to you is totally not your concern.

All in all, the overall scheme is as follows:

     class T
     {
         struct info_t
         {
             std::size_t m_Count;
         };

         struct alignment_gap_helper
         {
             info_t dummy1;
             T dummy2;
         };

         enum _
         {
             gap_size = sizeof(alignment_gap_helper) -
                 sizeof(info_t) - sizeof(T)
         };

     public:

         static void* operator new[] (std::size_t size)
         {
             void* p = std::malloc(sizeof(info_t) + gap_size + size);
             if (p)
             {
                 new (p) info_t();
                 static_cast< info_t* >(p)->m_Count = size / sizeof(T);
                 // Return the adjusted pointer
                 return static_cast< char* >(p)
                     + sizeof(info_t) + gap_size;
             }
             else
                 throw std::bad_alloc();
         }

         static void operator delete[] (void* p)
         {
             if (p)
             {
                 // Restore the original pointer
                 p = static_cast< char* >(p)
                     - sizeof(info_t) - gap_size;
                 static_cast< info_t* >(p)->~info_t();
                 std::free(p);
             }
         }

         static void* operator new (std::size_t size)
         {
             return operator new[] (size);
         }

         static void operator delete (void* p)
         {
             return operator delete[] (p);
         }

         friend std::size_t element_count_of(T const* p)
         {
             if (p)
             {
                 // Restore the original pointer
                 p = static_cast< const char* >(p)
                     - sizeof(info_t) - gap_size;
                 return static_cast< const info_t* >(p)->m_Count;
             }
             else
                 return 0;
         }
     };

And you also should not forget the operator overloads with std::nothrow
argument.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk