What's new

Templates

Doomulation

?????????????????????????
A simple question.
Is it possible, with templates, to actually deduce, or know, the type of the argument passed to a function? In a little sample, say a Registry class with the function to retrieve something from the registry.

The function would take a buffer. With this information, it would be able to automatically aquire the size of the buffer (if it is a normal pointer, it would be able to aquire the size, but not with arrays, like strings!).
Furthermore, it would be able to, for example, check if the data retrieved is REG_SZ, the buffer needs to be of type CString* or char*. Since the types of arguments passed to a template function are known to the compiler, this shouldn't be impossible, should it?

Is there a way?
 

smcd

Active member
If you have RTTI enabled, there is an operator, typeid.

Code:
#include<typeinfo>
...
if(typeid(*some_ptr) == typeid(CString))
{
}
else if(typeid(*some_ptr) == typeid(char*))
{
}
// i think it goes something like the above - check The C++ Programming Language book or online references
 
Last edited:

Blargg

New member
I think overloading will serve you better here. Just write overloaded functions to handle the different buffer types. These could then be called from a function template which allowed any type.

Code:
void handle_buffer( char* );
void handle_buffer( CString* );

template<class T>
void register( T buffer )
{
	// ...
	handle_buffer( buffer );
	// ...
}

RTTI won't help for built-in types or user-defined types that don't have any virtual functions, since the object's type isn't encoded anywhere at run-time; in this case the static type of the object is returned (so if the object is a derived type, typeid() won't tell you about it). If you really need to know the static type of an object at compile-time, take a look at Boost's type_traits library, specifically is_same<T,U>::value which evaluates to true if types T and U are the same, false if they are not. For example,

boost::is_same<int, int>::value is true
boost::is_same<int, char>::value is false

I think these basically specialize a simple template:

Code:
struct true_result { static const int value = true; };
struct false_result { static const int value = false; };

template<typename T,typename U>
struct is_same : false_result { };

template<typename T>
struct is_same<T,T> : true_result { };

If you'd post more details about your design, we might find other approaches.
 
OP
Doomulation

Doomulation

?????????????????????????
No, I think typeid will do very well. It does everything I want and has allowed me to create a powerful and safe Registry class.
Lo and behold my improved registry class with typeid:

Code:
	long GetRegValuePtr(const char* strKey, const char* strName, T1* pBuffer, bool bAllocate, T2* pRet = THROW_ERROR, DWORD* pType = NULL) throw(...)
	{
		long lErr;
		bool bBufferIsCString, bRetIsCString;
		bool bBufferIsChar, bRetIsChar;
		DWORD nBufferSize, nRetSize;
		bBufferIsCString = bRetIsCString = bBufferIsChar = bRetIsChar = false;

		// Buffer size
		if ( typeid(pBuffer) == typeid(const char*) ||
				typeid(pBuffer) == typeid(char*) )
		{
				nBufferSize = (DWORD)strlen((const char*)pBuffer);
				bBufferIsChar = true;
		}
		else if ( typeid(pBuffer) == typeid(CString*) )
		{
			nBufferSize = ((CString*)pBuffer)->GetLength() + 1;
			bBufferIsCString = true;
		}
		else
			nBufferSize = sizeof(*pBuffer);

		// Return size
		if ( typeid(pRet) == typeid(const char*) ||
				typeid(pRet) == typeid(char*) )
		{
				nRetSize = (DWORD)strlen((const char*)pRet);
				bRetIsChar = true;
		}
		else if ( typeid(pBuffer) == typeid(CString*) )
		{
			nRetSize = ((CString*)pRet)->GetLength() + 1;
			bRetIsCString = true;
		}
		else
			nRetSize = sizeof(*pRet);

		try
		{
			lErr = OpenKey(strKey, true);
			if (lErr != ERROR_SUCCESS) return ThrowError(lErr);
		}
		catch(long lErr)
		{
			if (lErr != ERROR_FILE_NOT_FOUND) return ThrowError(lErr);
			goto File_Not_Found;
		}

		DWORD dwType, nRequiredAmount;
		lErr = RegQueryValueEx(m_hKey, strName, NULL, &dwType, NULL, &nRequiredAmount);
		if (lErr == ERROR_FILE_NOT_FOUND)
			goto File_Not_Found;
		else if (lErr != S_OK)
			return ThrowError(lErr);
		
		// To make sure if no buffer is provided, go no further.
		if (pBuffer == NULL) return ERROR_FILE_NOT_FOUND;

		if (pType) *pType = dwType;
		BYTE* pNewBuffer;

		if (dwType == REG_SZ)
		{
			// If the registry value type is REG_SZ (string), then the buffer must be char*,
			// const char* or CString* to hold the data.
			ASSERT(bBufferIsCString || bBufferIsChar);

			if (bAllocate)
			{
				// Means that we should create the buffer
				// Make sure a compatible buffer has been passed
				ASSERT( typeid(pBuffer) == typeid(CString*) || typeid(pBuffer) == typeid(char**) ||
					typeid(pBuffer) == typeid(const char**) );

				if ( typeid(pBuffer) == typeid(CString*) )
					pNewBuffer = (BYTE*)((CString*)pBuffer)->GetBuffer(nRequiredAmount);
				else
				{
					BYTE** pTempBuffer = (BYTE**)pBuffer;
					*pTempBuffer = new BYTE[nRequiredAmount];
					pNewBuffer = *pTempBuffer;
				}

				nBufferSize = nRequiredAmount;
			}
		}
		else
			pNewBuffer = (BYTE*)pBuffer;

		// Must have enough data in buffer if we're not allocating.
		ASSERT(nBufferSize >= nRequiredAmount);

		lErr = RegQueryValueEx(m_hKey, strName, NULL, &dwType, (BYTE*)pNewBuffer, &nBufferSize);
		if (bBufferIsCString)
		{
			try
			{
				((CString*)pBuffer)->ReleaseBuffer();
			}
			catch(...)
			{
				// Bad data aquired from registry.
				((CString*)pBuffer)->Empty();
				if ((int)(__int64)pRet != THROW_ERROR) goto File_Not_Found;
				else return ThrowError(ERROR_INVALID_DATA);
			}
		}
		if (lErr != ERROR_SUCCESS) return ThrowError(lErr);
		if (lErr == ERROR_FILE_NOT_FOUND) goto File_Not_Found;
		return ERROR_SUCCESS;

File_Not_Found:
		if ((int)(__int64)pRet == THROW_ERROR) return ThrowError(lErr);
		if ( typeid(pBuffer) != typeid(CString*) )
		{
			// The buffer provided must be large enough to hold the return data!
			ASSERT(nBufferSize >= nRetSize);
			memcpy((void*)pBuffer, pRet, nRetSize);
		}
		else
		{
			CString* pString = (CString*)pBuffer;
			memcpy(pString->GetBuffer(nRetSize), pRet, nRetSize);
			pString->ReleaseBuffer();
		}
		return ERROR_FILE_NOT_FOUND;
	}
 

smcd

Active member
Thanks for the input Blargg, I've learned something ;) I'd never used typeid personally just knew it existed. Some parts of C++ are still unclear to me like its casting methods (some of which need RTTI I believe, dynamic_cast?)
 
OP
Doomulation

Doomulation

?????????????????????????
I tend to only use C-style casts. They always work for me. I've never really needed a C++-style cast, but if that would be the case, I would probably use static cast.
 

zenogais

New member
The C++ casts are just fancier, type-safe ways of casting. Personally I'm tending to use static_cast<> for almost everything anymore - it works just like C-style casts, but catches my casting errors. Most of the other casts I almost never need except occassionally dynamic_cast<>
 

Blargg

New member
The new-style casts simply break the multiple functions of a C-style cast into distinct operations. A C-style cast can basically do integral/floating-point conversions (implicit), const qualifier changes (const_cast), pointer casts from base to derived class, or void* to T* (static_cast), and pointer conversions between unrelated types (reinterpret_cast). By explicitly stating which operation you intend, you make it possible for the compiler to catch attempts to do something else. For example, if you merely meant to cast from base to derived but mistakenly had a pointer to an unrelated class, static_cast would cause a compilation error, while a C-style cast would quietly say "well, can't do static_cast, so I'll do reinterpret_cast!".

The only new functionality is that provided by dynamic_cast, which is like a static_cast but with a run-time check that the object really is of the derived type you're casting to; if it's not, you either get a null pointer (for a pointer cast) or a bad_cast exception (for a reference cast). Since in C++ you don't pay for what you don't use, dynamic_cast requires that the base class have at least one virtual function, since that is the only time C++ adds invisible data to an object to keep track of its type. The same holds for using typeid to determine the dynamic type of an object; if it has no virtual functions, it will return the static (compile-time) type instead.
 

smcd

Active member
That explanation makes much more sense than some others I've read in C++ books, thanks!
 

Top