[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c++

Re: This HAS to be UB...

Chris M. Thomasson

10/5/2008 1:37:00 AM


"Chris M. Thomasson" <no@spam.invalid> wrote in message news:...
> >
> "James Kanze" <james.kanze@gmail.com> wrote in message
> news:7878ab49-834f-4bbc-b687-efdd8f31f1f3@z66g2000hsc.googlegroups.com...
>> On Oct 2, 9:52 pm, "Chris M. Thomasson" <n...@spam.invalid> wrote:
>> > Keep in mind that I am a C programmer; well, anyway here is
>> > the C++ program...
>
>> It looks to me like you're attacking some fairly tricky stuff.
>> You'd probably be better of starting with something simpler if
>> you're still learning C++. However...
>
> I was exploring the feature in C++ delete operator in which the size of
> the allocation is returned along with the pointer to allocated memory. One
> could create heavily optimized custom memory allocator using that
> important piece of information.
>
>
>
>
>> > ______________________________________________________________________
>> > #include <cstdio>
>> > #include <cstdlib>
>> > #include <new>
>
>> > struct custom_allocator {
>> > static void* allocate(std::size_t size)
>> > throw(std::bad_alloc()) {
>
>> That should doubtlessly be:
>> throw( std::bad_alloc )
>> What you've said is that the only exception type which will
>> escape from your function is a pointer to a function returning
>> an std::bad_alloc and taking no arguments. I really don't think
>> you meant to say that you're going to throw pointers to
>> functions.
>
> That was definitely a typo/error on my part.
>
>
>
>
>> In practice, exception specifications are not really that
>> useful, except when they're empty. (It's very important in
>> certain cases to know that a function cannot throw any
>> exceptions, but it's rarely useful to know that it can't throw
>> certain types of exceptions.)
>
> I thought it would be prudent to give the overloaded operator new an
> exception specification of `std::bad_alloc'. Also, I wanted to give an
> empty specification to the overload of operator delete. As to how useful
> it is... Well, I don't quite know.
>
>
>
>
>> > void* const mem = ::operator new(size);
>> > std::printf("custom_allocator::allocate(%p, %lu)\n",
>> > (void*)mem, (unsigned long)size);
>> > return mem;
>> > }
>
>> > static void deallocate(void* const mem, std::size_t size)
>> > throw() {
>> > std::printf("custom_allocator::deallocate(%p, %lu)\n",
>> > (void*)mem, (unsigned long)size);
>> > ::operator delete(mem);
>> > }
>> > };
>
>> > template<typename T>
>> > struct allocator_base {
>> > static void* operator new(std::size_t size)
>
>> The static isn't really necessary: allocation and deallocation
>> member functions (operator new and operator delete) are always
>> static, whether you declare them so or not. (On the other hand,
>> it doesn't hurt.)
>
> Its a habit of mine. Also, using printf in C++ is another habit.
>
>
>
>
>> > throw(std::bad_alloc()) {
>> > return custom_allocator::allocate(size);
>> > }
>
>> > static void* operator new[](std::size_t size)
>> > throw(std::bad_alloc()) {
>> > return custom_allocator::allocate(size);
>> > }
>
>> > static void operator delete(void* mem)
>
>> Just curious: since you require the size in delete[], why don't
>> you require it here? Derivation can mean that the size isn't a
>> constant, e.g.:
>>
>> class Base : public allocator_base< Base >
>> {
>> // ...
>> } ;
>>
>> class Derived : public Base
>> {
>> // ...
>> } ;
>>
>> Base* p = new Derived ;
>> // ...
>> delete p ;
>
>> (This supposes, of course, that Base has a virtual destructor.)
>
>
>
>
> [...]
>
>> > ______________________________________________________________________
>
>> > On GCC I get the following output:
>
>> > custom_allocator::allocate(00246C50, 2234)
>> > custom_allocator::deallocate(00246C50, 2234)
>> > custom_allocator::allocate(00247760, 11174)
>> > custom_allocator::deallocate(00247760, 11174)
>
>> > On MSVC 8 I get:
>
>> > custom_allocator::allocate(00362850, 2234)
>> > custom_allocator::deallocate(00362850, 2234)
>> > custom_allocator::allocate(00366B68, 11170)
>> > custom_allocator::deallocate(00366B68, 2234)
>
>> > Are they both right due to UB? WTF is going on? GCC seems to
>> > be accurate at least... DAMN!
>
>> Well, there's no undefined behavior. You're program seems
>> perfectly legal and well defined to me. It looks like a bug in
>> VC++, see §12.5/5:
>
> It definitely looks like a bug is MSVC++. I get erroneous behavior on
> versions 6 through 9.
>
>
>
>
>> When a delete-expression is executed, the selected
>> deallocation function shall be called with the address
>> of the block of storage to be reclaimed as its first
>> argument and (if the two-parameter style is used) the
>> size of the block as its second argument.
>
>> And I can't think of any way of interpreting "the size of the
>> block" to mean anything other than the size requested in the
>> call to operator new.
>
> I thought that MSVC was crapping out because `allocator_base' was a
> template. So I created another little test which hopefully has all the
> bugs fixed:
> __________________________________________________________________________
> #include <cstdio>
> #include <cstdlib>
> #include <new>
>
>
> struct custom_allocator {
> static void* allocate(std::size_t size)
> throw(std::bad_alloc) {
> void* const mem = std::malloc(size);
> if (! mem) {
> throw std::bad_alloc();
> }
> std::printf("custom_allocator::allocate(%p, %lu)\n",
> (void*)mem, (unsigned long)size);
> return mem;
> }
>
> static void deallocate(void* const mem, std::size_t size)
> throw() {
> if (mem) {
> std::printf("custom_allocator::deallocate(%p, %lu)\n",
> (void*)mem, (unsigned long)size);
> std::free(mem);
> }
> }
> };
>
>
> struct allocator_base {
> void* operator new(std::size_t size)
> throw(std::bad_alloc) {
> return custom_allocator::allocate(size);
> }
>
> void* operator new [](std::size_t size)
> throw(std::bad_alloc) {
> return custom_allocator::allocate(size);
> }
>
> void operator delete(void* mem, std::size_t size)
> throw() {
> custom_allocator::deallocate(mem, size);
> }
>
> void operator delete [](void* mem, std::size_t size)
> throw() {
> custom_allocator::deallocate(mem, size);
> }
> };
>
>
>
>
> template<std::size_t T_size>
> class buf : public allocator_base {
> char mem[T_size];
> public:
> virtual ~buf() throw() {}
> };
>
>
> class buf2 : public buf<1234> {
> char mem2[1000];
> };
>
>
> int main() {
> buf<1024>* b1 = new buf<1024>;
> delete b1;
>
> buf2* b2 = new buf2;
> delete b2;
>
> b2 = new buf2[5];
> delete [] b2;
>
> return 0;
> }
>
> __________________________________________________________________________
>
>
>
>
> On every version of GCC I have, I get the following output on a 32-bit
> machine:
>
> custom_allocator::allocate(00246C50, 1028)
> custom_allocator::deallocate(00246C50, 1028)
> custom_allocator::allocate(002472A8, 2240)
> custom_allocator::deallocate(002472A8, 2240)
> custom_allocator::allocate(002472A8, 11204)
> custom_allocator::deallocate(002472A8, 11204)
>
>
>
>
> On every version of MSVC, I get:
>
> custom_allocator::allocate(00365B28, 1028)
> custom_allocator::deallocate(00365B28, 1028)
> custom_allocator::allocate(00362850, 2240)
> custom_allocator::deallocate(00362850, 2240)
> custom_allocator::allocate(00366FA8, 11204)
> custom_allocator::deallocate(00366FA8, 2240)
>
>
>
> Well, MSVC has a fairly nasty bug indeed. Anyway, what do you think James?