|
Boost : |
Subject: Re: [boost] How to get the size of an C-array?
From: vicente.botet (vicente.botet_at_[hidden])
Date: 2009-10-15 15:26:35
Hi,
----- Original Message -----
From: "Andrey Semashev" <andrey.semashev_at_[hidden]>
To: <boost_at_[hidden]>
Sent: Thursday, October 15, 2009 7:40 PM
Subject: Re: [boost] How to get the size of an C-array?
>
> 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?
Yes. This is the case I try to solve.
>>> 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.
Yes, this is the case. I pretend to pverload only the new/delete operators for specific classes.
>> 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);
Are you sure the size given as parameter consider only the user info? Doesn't this size includes any additional part needed by the standard library to know if the the allocation corresponds to a single element or to an array and whatever is needed to call the destructors of all the array elements?
> // 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.
Yes. Thanks to recal this to me. I was already aware of this kind of problems.
All this stuff seems correct except the way you calculate the m_Count.
Thanks you Andrey to try to solve this problem and for all the details.
Best,
Vicente
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk