Template overload of operator ++/--

This is a discussion on Template overload of operator ++/-- within the C++ Programming forums, part of the General Programming Boards category; Originally Posted by Elysia It's a single class to hold multiple values in one big huge block of memory, by ...

  1. #16
    Registered User
    Join Date
    Apr 2006
    Posts
    2,053
    Quote Originally Posted by Elysia View Post
    It's a single class to hold multiple values in one big huge block of memory, by converting it to BYTE* (ie raw) and storing it along with type information.
    Oh, I see. So you're reinventing the Heap.

    Then you just have to make the pointers/iterators and subscript operators use that type information information to figure out how to move to the next element. No templates needed, because the type information is stored with the objects.

    You may need templates when you actually dereference your pointers/iterators, to make them return the correct type. Alternatively you can still make operators * and -> work by returning a generic object type that can be cast to any object. Or just use BYTE.
    Last edited by King Mir; 10-21-2007 at 05:16 PM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  2. #17
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,897
    Maybe. I figured it was the only way to store type information since some operators can't be made with templates.
    The only big hurdle I see right now is that I have to manually check for type and cast it to the right type. This is especially important with pointers since they reference a fixed amount of memory.
    I figure I have a lot to think about in making such a class. For now, I made a much simpler ThreadSafetyInt class to lock and unlock before fetching/saving data. Although strings are a little more complex.

  3. #18
    Registered User
    Join Date
    Apr 2006
    Posts
    2,053
    Quote Originally Posted by Elysia View Post

    Code:
    class CSyncClass
    {
    public:
    	template<typename T> eDataType GetDataType(T tData);
    	template<typename T> DWORD GetSize(T& tData);
    	template<typename T> bool IsNumType(T& tData);
    };
    BTW, these 3 functions could easily be made into compile time computations, instead of runtime:
    Code:
    #define MK_GET_DATA_TYPE(T)  eDataType GetDataType(T tData) { \
            return sizeof(TYPE_##T);                               \
         }
    
        MK_GET_DATA_TYPE(CHAR);
        MK_GET_DATA_TYPE(TYPE_CSTRINGP);
    #undef MK_GET_DATA_TYPE
        template<typename T> DWORD GetSize(T& tData){return sizeof(T)}
        // use a combination of the above to implement IsNumType
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  4. #19
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,897
    GetDataType would usually try to inquire the type of the data and return appropriate enum type. It's just that it can't be done at compile time since data is stored raw and hence all type information (except for what I store) is gone.
    I can't see how that helps.
    Further, the GetSize function you write is lacking since if the data type is an array, a string or a class such as CString, it will return faulty length.
    But then again, I fear that I didn't design the class to handle arrays -_-
    Last edited by Elysia; 10-21-2007 at 09:44 PM.

  5. #20
    Registered User
    Join Date
    Apr 2006
    Posts
    2,053
    Quote Originally Posted by Elysia View Post
    GetDataType would usually try to inquire the type of the data and return appropriate enum type. It's just that it can't be done at compile time since data is stored raw and hence all type information (except for what I store) is gone.
    I can't see how that helps.
    My mistake, I assumed it just converted a type to it's enum value.
    Further, the GetSize function you write is lacking since if the data type is an array, a string or a class such as CString, it will return faulty length.
    But then again, I fear that I didn't design the class to handle arrays -_-
    If it is an array or string you have to explicitly specify the special case:
    Code:
    template<typename T> DWORD GetSize(T *tData){
        //assume 0 element is size.(as an example)
        return tData[0]
    };
    template<> DWORD GetSize(STRCLASS tData){
        return tData.size();
    };
    Same approach goes for writing IsNumType.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  6. #21
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,897
    Quote Originally Posted by King Mir View Post
    My mistake, I assumed it just converted a type to it's enum value.
    Code:
    template<typename T> CSyncClass::eDataType CSyncClass::GetDataType(T tData)
    {
    	if ( typeid(tData) == typeid(CHAR) ) return TYPE_CHAR;
    	else if ( typeid(tData) == typeid(BYTE) ) return TYPE_BYTE;
    	else if ( typeid(tData) == typeid(INT16) ) return TYPE_INT16;
    	else if ( typeid(tData) == typeid(UINT16) ) return TYPE_UINT16;
    	else if ( typeid(tData) == typeid(INT32) ) return TYPE_INT32;
    	else if ( typeid(tData) == typeid(UINT32) ) return TYPE_UINT32;
    	else if ( typeid(tData) == typeid(INT64) ) return TYPE_INT64;
    	else if ( typeid(tData) == typeid(UINT64) ) return TYPE_UINT64;
    	else if ( typeid(tData) == typeid(CString) ) return TYPE_CSTRING;
    	else if ( typeid(tData) == typeid(CHAR*) ) return TYPE_CHARP;
    	else if ( typeid(tData) == typeid(BYTE*) ) return TYPE_BYTEP;
    	else if ( typeid(tData) == typeid(INT16*) ) return TYPE_INT16P;
    	else if ( typeid(tData) == typeid(UINT16*) ) return TYPE_UINT16P;
    	else if ( typeid(tData) == typeid(INT32*) ) return TYPE_INT32P;
    	else if ( typeid(tData) == typeid(UINT32*) ) return TYPE_UINT32P;
    	else if ( typeid(tData) == typeid(INT64*) ) return TYPE_INT64P;
    	else if ( typeid(tData) == typeid(UINT64*) ) return TYPE_UINT64P;
    	else if ( typeid(tData) == typeid(CString*) ) return TYPE_CSTRINGP;
    }
    If it is an array or string you have to explicitly specify the special case:
    Code:
    template<typename T> DWORD GetSize(T *tData){
        //assume 0 element is size.(as an example)
        return tData[0]
    };
    template<> DWORD GetSize(STRCLASS tData){
        return tData.size();
    };
    Same approach goes for writing IsNumType.
    If my memory serves me, the function is used to determine the size of the data/buffer passed, so long as it's not an array (will have to refine ideas on that one, though I don't know if it may be beyond the scope of the class). It uses the returned size the set the block size and know how much data to allocate. Current implemtation:

    Code:
    template<typename T> DWORD CSyncClass::GetSize(T& tData)
    {
    	DWORD dwSize;
    	if ( IsNumType(tData) )
    	{
    		 nSize = sizeof(tData);
    	}
    	else if ( typeid(tData) == typeid(CHAR*) )
    	{
    		nSize = strlen((CHAR*)tData) + 1; // Be sure to include the NULL char!
    	}
    	else if ( typeid(tData) == typeid(CString) )
    	{
    		ASSERT(FALSE); // CStrings not supported!
    		nSize = ((CString*)&tData)->GetLength();
    	}
    	else if ( typeid(tData) == typeid(CString*) )
    	{
    		ASSERT(FALSE); // CStrings not supported!
    		nSize = ((CString*)tData)->GetLength();
    	}
    	ASSERT(dwSize > 0);
    	return dwSize;
    }

  7. #22
    Registered User
    Join Date
    Apr 2006
    Posts
    2,053
    So are these Compile time or runtime checks?

    If compile time, then my implementation works better.

    If runtime, then the parameter passed will not be of the type of data it is pointing to -- it will instead be of the type of the generic object (which you said was BYTE). To conclude what the type is, you will have to read the type information from where ever in memory you store it.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  8. #23
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,897
    Runtime.
    Both functions are executed BEFORE the data is stored. They're used to gather information used in storing the data. GetSize is called first to check the actual size of the data. Then GetDataType is called to attain the type the data is. Those two values are stored in a struct when saving the value.
    There are no post-checks after retrieving the data, but I guess I will need to do that. But I won't use those functions for that because there's no runtime data after retrieving stored data. I could post the entire saving function, but it's pretty long. But here it is anyway, if you want to have a look at it:

    Code:
    template<typename T> void CSyncClass::SetValue(CString strName, T tData, UINT32 nSize)
    {
    	cSync.Lock();
    	nSize = GetSize(tData); // Get data size
    	CBufferValueInfo* pValue = NULL;
    
    	// Does a value of this type already exist?
    	m_LookupMap.Lookup(strName, (void*&)pValue);
    	if (pValue != NULL)
    	{
    		// There exists a value with this name already. Check size. If size is equal, then go ahead and replace it. If not...
    		if (pValue->nSize == nSize) goto AssignValue;
    		if (pValue->nSize < nSize)
    		{
    			// The existing block isn't large enough to hold our data! Let's free it and find a new block.
    			pValue->bAllocated = false;
    			pValue->strValueName = "";
    			pValue = NULL;
    			UINT32 nOffset = 0;
    
    			for(;;)
    			{
    				// Loop for finding a new block
    				m_BlockLookupMap.Lookup(nOffset, pValue);
    				if (pValue == NULL)
    				{
    					// The map is empty or we found the end of the map! Create a new block and continue.
    					pValue = new CBufferValueInfo;
    					pValue->nOffset = nOffset;
    					goto AssignValue;
    				}
    				else
    				{
    					// We found an existing block. If it's allocated already, ignore it. If it's free, check if it's big enough.
    					if (pValue->bAllocated)
    					{
    						nOffset += pValue->nSize; // Jump ahead to the beginning of the next block.
    						continue;
    					}
    					else
    					{
    						// Block is empty, but is it large enough?
    						if (pValue->nSize == nSize)
    						{
    							// Block is exactly the right size to hold our data, so go ahead and assign the data!
    							pValue = new CBufferValueInfo;
    							pValue->nOffset = nOffset;
    							goto AssignValue;
    						}
    						if (pValue->nSize > nSize)
    						{
    							// Block is bigger than the data it needs to hold, so let's split it.
    							CBufferValueInfo* pTemp = new CBufferValueInfo;
    							pTemp->nOffset = pValue->nOffset + nSize;
    							pTemp->nSize = pValue->nSize - nSize;
    							pTemp->bAllocated = false;
    							m_BlockLookupMap.SetAt(pTemp->nOffset, pTemp);
    							pTemp = NULL;
    							pValue->nSize = nSize;
    							goto AssignValue;
    						}
    						if (pValue->nSize < nSize)
    						{
    							// Block isn't large enough to hold our data! So let's check if there's another free block ahead we can use as well. If not, we'll continue searching.
    							CBufferValueInfo* pTemp = NULL;
    							m_BlockLookupMap.Lookup(pValue->nOffset + pValue->nSize, pTemp);
    							if (pTemp == NULL)
    							{
    								// There's no more blocks available. Extend the size of this one and continue with the assigning process!
    								pValue->nSize = nSize;
    								goto AssignValue;
    							}
    							else
    							{
    								// There's another block here! Let's examine it.
    								if (pTemp->bAllocated)
    								{
    									// No luck. Block is already taken.
    									nOffset += pValue->nSize;
    									continue;
    								}
    								else
    								{
    									// Block is free! Let's merge these two and continue the search!
    									pValue->nSize += pTemp->nSize;
    									m_BlockLookupMap.RemoveKey(pTemp->nOffset);
    									continue;
    								}
    							}
    						}
    					}
    				}
    			}
    		}
    		if (pValue->nSize > nSize)
    		{
    			// Block is large enough to hold our data - in fact, it's a little too big. We need to split the block and free the memory we don't need.
    			CBufferValueInfo* pTemp = new CBufferValueInfo;
    			pTemp->nOffset = pValue->nOffset + nSize;
    			pTemp->nSize = pValue->nSize - nSize;
    			pTemp->bAllocated = false;
    			pValue->nSize = nSize;
    			m_BlockLookupMap.SetAt(pTemp->nOffset, pTemp);
    			goto AssignValue;
    		}
    	}
    	
    	// Find a free block, or if none such exist, create one
    	
    AssignValue:
    	// Let's create out index and store it in the maps
    	pValue->nSize = nSize;
    	pValue->strValueName = strName;
    	pValue->bAllocated = true;
    	pValue->DataType = GetDataType(tData);
    	m_LookupMap.SetAt(strName, (void*)pValue);
    	m_BlockLookupMap.SetAt(pValue->nOffset, pValue);
    
    	// Is the offset of the data larger than our current buffer size? If so, then we need to resize it!
    	if (pValue->nOffset > m_nBufferSize)
    	{
    		m_nBufferSize = pValue->nOffset - m_nBufferSize + 100;
    		m_pBuffer.Resize(m_nBufferSize);
    	}
    
    	// Everything set then? Let's copy the data to our buffer!
    	//if ( typeid(tData) ==
    	memcpy_s(m_pBuffer[pValue->nOffset], m_nBufferSize - pValue->nOffset, tData, nSize);
    	cSync.Unlock();
    }
    Last edited by Elysia; 10-22-2007 at 02:55 PM.

  9. #24
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by King Mir View Post
    So are these Compile time or runtime checks?

    If compile time, then my implementation works better.

    If runtime, then the parameter passed will not be of the type of data it is pointing to -- it will instead be of the type of the generic object (which you said was BYTE). To conclude what the type is, you will have to read the type information from where ever in memory you store it.
    These tests are done at runtime.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  10. #25
    Registered User
    Join Date
    Apr 2006
    Posts
    2,053
    No, look, if you use a template, then the test is done at compile time for that function. You can still pass an generic type to it at run type if you cast it first (implicitly or explicitly). But since any cast is done compile time, the function knows which version to call. For example you can do this:
    Code:
    void genericIncrement(GENERIC_TYPE tData){
        int type= getType(tData);
        stitch(type){
            case TYPE_INT: ++((INT)tData);
            case TYPE_CHAR: ++((CHAR)tData);
            case TYPE_CSTR: (CSTR)tData ( (CSTR)tData).size()-1)++ //increment last member
            default: throw MyException("type data corupted");
        }
    }
    Elysia, in the code you just posted, since SetValue is a template, all tests on the type are done compile time; when you call set Value you must already know the type T. As such the previously mentioned overloaded GetDataType function and specialized templated GetSize should be used.

    But if you want the class to manage the multiple Type, you still need to have a function/meathod/class that reads the type information and casts it to the correct type. Once the type is deduced, the template functions can be called.

    So typeid() should not be used AT ALL. It will not help you here. Either you know the type T, in which case you should use overloaded functions and specialized templates to operate on the type. Or must deduce the Type form wherever your enum eDataType is stored. Typeid is used only in inheritance, to deduce the child object from a reference or pointer to its parent. Since you are not using inheritance, you have no use for typeid. Even if you do use inheritance in your code, there is still unlikely to be a use for typeid, because typeid breaks the polymorphic model.
    Last edited by King Mir; 10-22-2007 at 08:43 PM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  11. #26
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Posts
    22,897
    What you do say has some truth to it... I never thought about it, but some functions such as GetSize or GetDataType could be overloaded similarly as to what you propose at compile time.

    I will look into it. But the main work lies in casting to the correct type to modify the data.

  12. #27
    Registered User
    Join Date
    Apr 2006
    Posts
    2,053
    Quote Originally Posted by Elysia View Post
    But the main work lies in casting to the correct type to modify the data.
    When you modify the data, You should already know the correct type. A a cast is involved, but it is only done once -- a simple get<TYPE>() function could easily cast the BYTE* (or void *, or your own personal pointer class) to the correct pointer type. Optionally, it could check if the type is correct and throw an exception or something if it isn't.

    EDIT: Unless that is the type really is determined runtime. This would mean that the program user, some runtime determinable file, or a network connection specifies the data type. This would apply to things like parsers

    If you want to provide a generic interphase to your data, this is separate from the task of managing where it is located and ensuring synchronized thread safe access.
    Last edited by King Mir; 10-23-2007 at 09:54 AM.
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

Page 2 of 2 FirstFirst 12
Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Smart pointer class
    By Elysia in forum C++ Programming
    Replies: 63
    Last Post: 11-03-2007, 08:05 AM
  2. matrix class
    By shuo in forum C++ Programming
    Replies: 2
    Last Post: 07-13-2007, 02:03 AM
  3. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 03:39 PM
  4. error: template with C linkage
    By michaels-r in forum C++ Programming
    Replies: 3
    Last Post: 05-17-2006, 09:11 AM
  5. oh me oh my hash maps up the wazoo
    By DarkDays in forum C++ Programming
    Replies: 5
    Last Post: 11-30-2001, 12:54 PM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21