
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.