I've got a managed C++ project in it I have an unmanaged piece of code.
In the unmanaged part I have an enum. When I call the enums destructor directly in the old project – no problems. But when I call it within the managed project it replaces it with System::Enum which does not have a destructor.
What can I do? Are all enums in managed C++ changed to System::Enum? Can I tell the compiler not to replace the old enum?
Example of the code (works in normal C++ but not in managed C++):
CGrowableArray has an array of TYPEs called m_pData; so for index i you can do
m_pData[i].~TYPE();
new (&m_pData[i]) TYPE;
no destructor is called twice... But if TYPE is an enum I have a problem.
darwen
October 14th, 2004, 05:28 AM
What are you doing ? Since when did enums have destructors ? Enums are values, and in C++ they're just integer values at that.
You shouldn't call destructors in C++ directly anyway : that's what 'delete' is for.
I really don't understand what you're on about here. If you really are doing these things you're breaking just about every rule in the book in native C++.
At any rate, you don't really need to worry about memory allocation in C++.NET because it's all taken care of for you. All you need to do is to 'delete' any NATIVE classes which you create. All __gc (i.e. .NET) classes are automatically cleaned up for you.
Darwen.
wien
October 14th, 2004, 11:07 PM
What are you doing ? Since when did enums have destructors ? Enums are values, and in C++ they're just integer values at that.That's what I thought too, but a call to an enum's destructor does actually compile on VC 7.1/GCC 3.3 so I don't quite know what to think. I don't see what they would be good for though. Nor do I see any reason to call them explicitly.
Does anyone know what the standard says on this subject?
OrenGL
October 15th, 2004, 05:34 AM
Well I see this has gone completely OT, which is ok, discussing C++/C is always good.
The reason the destructor for the enum is called is that it can be. The template holds 'typename' and it does not know if it will be a class, struct or in our case an enum. The destructor is called and if it does nothing, so be it.
You shouldn't call destructors in C++ directly anyway : that's what 'delete' is for.
Thing is I don't want to give back the memory at that point, just want to destroy the object and make a new one in it's place.
At any rate, you don't really need to worry about memory allocation in C++.NET because it's all taken care of for you. All you need to do is to 'delete' any NATIVE classes which you create. All __gc (i.e. .NET) classes are automatically cleaned up for you.
That’s what I've been trying to do, I'm just having a namespace problem with this enum / System::enum type.
MrViggy
October 15th, 2004, 11:28 AM
Funny, I can't seem to find any reference to a destructor for enumerations in the 1998 version of the spec, and I can't seem to find any reference to this in Stroustrup's book...
I wonder if Microsoft decided to implement an enumeration as a specialized "class"?
Viggy
wien
October 15th, 2004, 06:49 PM
I wonder if Microsoft decided to implement an enumeration as a specialized "class"?It works on GCC too, so it doesn't seem to be Microsoft specific. :confused:
darwen
October 15th, 2004, 09:14 PM
My point is that it's not a safe thing to do which is why you're having problems.
I can't see why you can't call the destructor on something - after all it is just a method. It's just bad practice to do so.
"Just because we can do a thing doesn't mean we necessarily should do that thing". (Know what film this is from ?).
Just do it the normal way, adhere to the rules and you should be ok.
Darwen.
MrViggy
October 18th, 2004, 12:37 PM
Oh, I totally agree, darwen, but it is rather interesting that some compilers would support such a construct. Unless they are treating all classes, structs, enums, and namespaces the "same"...
Viggy
darwen
October 18th, 2004, 06:05 PM
I'm sure it's a compiler idioisynchrasy. In fact as I've said, a destructor is just a function like any other function in the V-Table and so therefore there's no real reason why you can't call it directly - in compiler parsing terms.
Let's just say that it's like jumping off a cliff to get to a beach. Yes you can do it - and it's quicker than using the path - but it sure does hurt.
Darwen.
OrenGL
October 18th, 2004, 06:22 PM
I don't get what you're all fussed up about. The code works find in C/C++. I agree that calling a destructor directly is not nice, but it happens to be a nice elegant solution in this case. I will post the code here so you can have a look; it is part of the DirectX9.0c SDK. BTW I'd still like to know the solution to the problem I had if anyone knows (how to make sure the normal enum destructor is called for an unmanaged C++ class in a MC++ wrapper).
Have a look at SetSize for an example of where the enum destructor might be used.
The code:
//--------------------------------------------------------------------------------------
// A growable array
//--------------------------------------------------------------------------------------
template< typename TYPE >
class CGrowableArray
{
public:
CGrowableArray() { m_pData = NULL; m_nSize = 0; m_nMaxSize = 0; }
CGrowableArray( const CGrowableArray<TYPE>& a ) { for( int i=0; i < a.m_nSize; i++ ) Add( a.m_pData[i] ); }
~CGrowableArray() { RemoveAll(); }
CGrowableArray& operator=( const CGrowableArray<TYPE>& a ) { if( this == &a ) return *this; RemoveAll(); for( int i=0; i < a.m_nSize; i++ ) Add( a.m_pData[i] ); return *this; }
HRESULT SetSize( int nNewMaxSize );
HRESULT Add( const TYPE& value );
HRESULT Insert( int nIndex, const TYPE& value );
HRESULT SetAt( int nIndex, const TYPE& value );
TYPE& GetAt( int nIndex ) { assert( nIndex >= 0 && nIndex < m_nSize ); return m_pData[nIndex]; }
int GetSize() const { return m_nSize; }
TYPE* GetData() { return m_pData; }
bool Contains( const TYPE& value ){ return ( -1 != IndexOf( value ) ); }
int IndexOf( const TYPE& value ) { return ( m_nSize > 0 ) ? IndexOf( value, 0, m_nSize ) : -1; }
int IndexOf( const TYPE& value, int iStart ) { return IndexOf( value, iStart, m_nSize - iStart ); }
int IndexOf( const TYPE& value, int nIndex, int nNumElements );
int LastIndexOf( const TYPE& value ) { return ( m_nSize > 0 ) ? LastIndexOf( value, m_nSize-1, m_nSize ) : -1; }
int LastIndexOf( const TYPE& value, int nIndex ) { return LastIndexOf( value, nIndex, nIndex+1 ); }
int LastIndexOf( const TYPE& value, int nIndex, int nNumElements );
HRESULT Remove( int nIndex );
void RemoveAll() { SetSize(0); }
protected:
TYPE* m_pData; // the actual array of data
int m_nSize; // # of elements (upperBound - 1)
int m_nMaxSize; // max allocated
HRESULT SetSizeInternal( int nNewMaxSize ); // This version doesn't call ctor or dtor.
};
//--------------------------------------------------------------------------------------
// Implementation of CGrowableArray
//--------------------------------------------------------------------------------------
// This version doesn't call ctor or dtor.
template< typename TYPE >
HRESULT CGrowableArray<TYPE>::SetSizeInternal( int nNewMaxSize )
{
if( nNewMaxSize < 0 )
{
assert( false );
return E_INVALIDARG;
}
//--------------------------------------------------------------------------------------
template< typename TYPE >
HRESULT CGrowableArray<TYPE>::SetSize( int nNewMaxSize )
{
int nOldSize = m_nSize;
for( int i = nNewMaxSize; i < nOldSize; ++i )
m_pData[i].~TYPE();
}
// Adjust buffer. Note that there's no need to check for error
// since if it happens, nOldSize == nNewMaxSize will be true.)
HRESULT hr = SetSizeInternal( nNewMaxSize );
//--------------------------------------------------------------------------------------
// Searches for the specified value and returns the index of the first occurrence
// within the section of the data array that extends from iStart and contains the
// specified number of elements. Returns -1 if value is not found within the given
// section.
//--------------------------------------------------------------------------------------
template< typename TYPE >
int CGrowableArray<TYPE>::IndexOf( const TYPE& value, int iStart, int nNumElements )
{
// Validate arguments
if( iStart < 0 ||
iStart >= m_nSize ||
nNumElements < 0 ||
iStart + nNumElements > m_nSize )
{
assert( false );
return -1;
}
// Search
for( int i = iStart; i < (iStart + nNumElements); i++ )
{
if( value == m_pData[i] )
return i;
}
// Not found
return -1;
}
//--------------------------------------------------------------------------------------
// Searches for the specified value and returns the index of the last occurrence
// within the section of the data array that contains the specified number of elements
// and ends at iEnd. Returns -1 if value is not found within the given section.
//--------------------------------------------------------------------------------------
template< typename TYPE >
int CGrowableArray<TYPE>::LastIndexOf( const TYPE& value, int iEnd, int nNumElements )
{
// Validate arguments
if( iEnd < 0 ||
iEnd >= m_nSize ||
nNumElements < 0 ||
iEnd - nNumElements < 0 )
{
assert( false );
return -1;
}
// Search
for( int i = iEnd; i > (iEnd - nNumElements); i-- )
{
if( value == m_pData[i] )
return i;
}
// Destruct the element to be removed
m_pData[nIndex].~TYPE();
// Compact the array and decrease the size
MoveMemory( &m_pData[nIndex], &m_pData[nIndex+1], sizeof(TYPE) * (m_nSize - (nIndex+1)) );
--m_nSize;
return S_OK;
}
darwen
October 18th, 2004, 06:31 PM
We're not getting fussed - we're just discussing why a compiler should allow something like this. Very interesting it has been too, thanks very much.
Anyway, I've already said my opinion - keep to C++ standard practices - so I think that's about it I'm afraid.
Darwen.
darwen
October 18th, 2004, 06:35 PM
I tell a lie - I looked at your code.
Most code which does stuff like this doesn't delete items if the requested memory reduces. There's not really any need for it unless you're going to be going from 200Mb to 2 bytes and never going back up again.
The efficiencies come from having the memory already allocated so if the size goes small, then large, then small, then large the memory doesn't need to be reallocated.
for( int i = nNewMaxSize; i < nOldSize; ++i )
m_pData[i].~TYPE();
}
can be removed completely and will highly probably speed up your application too.
Taking advantages of loopholes in compilers is a bad idea.
Stick to the standard and you'll be ok. Don't stick to the standard and you'll fall down so many holes.
I see code all the time which doesn't adhere to basic C++ programming standards when downloading free libraries (which is why I don't use them).
If you code to a discrepency, or even something specific to your compiler or operating system you will fall foul especially if you're trying to go cross platform.
Personally, I've always adhered to the C++ standard (I don't even use STL) and my programs converted, compiled and ran first time when I transferred them from VC6 to VC7 except for warnings about 64-bit.
Then again I like MFC and hate STL.
Oh and those included Direct3D apps too.
Darwen.
MrViggy
October 18th, 2004, 07:04 PM
I don't get what you're all fussed up about. The code works find in C/C++. I agree that calling a destructor directly is not nice, but it happens to be a nice elegant solution in this case. I will post the code here so you can have a look; it is part of the DirectX9.0c SDK. BTW I'd still like to know the solution to the problem I had if anyone knows (how to make sure the normal enum destructor is called for an unmanaged C++ class in a MC++ wrapper).
Have a look at SetSize for an example of where the enum destructor might be used.
Well, it looks like you're handling memory management directly in this example, so yes, it makes sence for you to call the DTOR directly.
But, I thought that according to the definition of an enumeration, it shouldn't have a DTOR (or a CTOR for that matter). So, (to me anyhow) calling a DTOR on an enum is undefined behavior. Either it is supported or it is not.
Viggy
OrenGL
October 18th, 2004, 07:31 PM
I'm sorry I might not have made this clear. This is not my code, this code was written as part of the DirectX 9.0c SDK from Microsoft so I don't take any credit for it, but I do think the SDK is nicely written non the less.
darwen
October 18th, 2004, 07:38 PM
One comment. No, the SDK is not well written.
Is it object oriented ?
No.
Is it nicely written ?
No.
Is it understandably written ?
No.
Does it depend on you having set up the directories on your whole DevStudio to look at specific directories.
Yes.
Are these directories relative ?
No.
Get the idea ? Nice code, i.e. elegant code just doesn't exist in their demos.
Oh well....
The best thing for the SDK to do was to take the approach "let's make things easy for people" which they certainly have not done.
Copying and pasting an application and then changing the parts of it you want to is not object oriented !!!!
I have to leave alone now because otherwise I'm going to get angry about people saying that the DirectX code is nicely written.
Darwen.
darwen
October 18th, 2004, 07:44 PM
Oh and if this is Microsoft code, then I suggest you take it up on their DirectX newsgroup.
I think you'll get loads of answers saying that their code is great.
Darwen.
codeguru.com
Copyright WebMediaBrands Inc., All Rights Reserved.