I'm wondering if there's a way to write a C++ template with different return types.
My use case is a method returning the largest values from a list.
But since I'm using the Qt framework, this function shall be able to deal with numeric and QString values. When feeding this function with a list of QString, the function shall return the length of the largest string. In case of passing numeric values, the input type shall be the return type.
What I've written is this:
template< class T >
auto getMax( QList< T > aList ) -> decltype( std::is_arithmetic< T >::value ? T : int( 0 ) )
{
if ( std::is_arithmetic< T >::value )
{
T Result( aList.isEmpty() ? 0 : aList.first() );
for ( auto lElement : aList )
{
Result = std::max( Result, lElement );
}
return Result;
}
if ( std::is_same< T, QString >::value )
{
// List contains QString -> return length of largest string
int Result( aList.isEmpty() ? 0 : aList.first().length() );
for ( const QString & lrcsElement : aList )
{
Result = std::max( lrcsElement.length(), Result );
}
return Result;
}
return 0;
}
This code compiles with VS 2017.
But when I want to use the template function like this
const QString sError ( tr( "Error" ) );
const QString sWarning( tr( "Warning" ) );
const QString sInfo ( tr( "Information" ) );
const QString sDebug ( tr( "Debug " ) );
auto iMaxTextLength( SMUtils::getMax< QString >( { sError, sWarning, sInfo, sDebug } ) );
the compiler gives me some error messages:
Error C2672: "SMUtils::getMax": no matching overloaded function found.
Error C2893: Failed to specialize function template "unknown-type SMUtils::getMax(QList)".
Error C2119: "li32MaxTextLength": the type for "auto" cannot be deduced from an empty initializer.
Of course I could write a specialized getMax( QStringList ) method, but I was wondering if it's possible to use only one template function.
Is that even possible and if so, how?
Thanks,
Sören
-> decltype( std::is_arithmetic< T >::value ? T : int( 0 ) )
should be
-> std::conditional_t<std::is_arithmetic<T>::value, T, int>;
or even omit it completely and let compiler deduce it (but requires correct return types, so following if constexpr).
and your
if ( std::is_arithmetic< T >::value )
should be
if constexpr ( std::is_arithmetic< T >::value )
In our base code, we are trying to replace one project defined class UtilHashTable by unordered_map to improve performance.
C'tor of UtilHashTable looks like:
template< class Key, class Val, class Hash >
UtilHashTable< Key, Val, Hash >::UtilHashTable( unsigned inInitialBuckets,
unsigned inMaxProbes ) :
cMaxProbes( inMaxProbes )
{
cLoadFactor = 0.5;
mSize = 0;
unsigned size = 1;
while( inInitialBuckets > 0 )
{
size <<= 1;
inInitialBuckets >>= 1;
}
//mTable = new vector< Entry >( size );
mTable = new unordered_map< Key, Val >( size );
mNumRehashes = 0;
mNumLookups = 0;
mNumProbes = 0;
mHashMask = static_cast< uint32>( mTable->size() - 1 );
}
In the header file, its declaration looks like:
UtilHashTable( unsigned inInitialBuckets = 500, unsigned inMaxProbes = 40 );
We are providing flexibility to the user to set InitialBuckets and MaxProbes if they want!
Please suggest me which c'tor of unordered_map will work best to replace everything inside the c'tor of UtilHashTable.
Tell me if you need any further info.
Thanks in advance! :D
I need to call a 3rd party library and pass in an int[3] as a void * like this [works]:
int pattern[3] = {2,4,10};
if ( OSTaskCreate( BlinkLED,
( void * ) pattern,
( void * ) &BlinkTaskStack[USER_TASK_STK_SIZE],
( void * ) BlinkTaskStack,
MAIN_PRIO - 1 ) != OS_NO_ERR )
{
iprintf( "*** Error creating blink task\r\n" );
}
But now I need to parse a string to get the pattern array and I can't seem to get it right.
First I pass the string into the parser and get back the array:
int (&ParseBlinkOnCommand(char rxbuffer[3]))[3]
{
// Code parses rxbuffer and creates the 3 ints needed
int pattern[3] = {repeats, onTicks, offTicks};
return pattern;
}
Then I try to pass it to the OSTaskCreate just like I did before:
int pattern2[3] = ParseBlinkOnCommand(rxbuffer);
if ( OSTaskCreate( BlinkLED,
( void * ) pattern2,
( void * ) &BlinkTaskStack[USER_TASK_STK_SIZE],
( void * ) BlinkTaskStack,
MAIN_PRIO - 1 ) != OS_NO_ERR )
{
iprintf( "*** Error creating remote blink task\r\n" );
}
but I get the error 'array must be initialized with a brace-enclosed initializer'.
What is the right way to do this?
First, ParseBlinkOnCommand returns reference to local object and so return dangling reference.
Second C-array are not copyable, so int pattern2[3] = ParseBlinkOnCommand(rxbuffer); should be int (&pattern2)[3] = ParseBlinkOnCommand(rxbuffer);.
but why not using std::vector or std::array (or custom structure) ?
std::vector<int> ParseBlinkOnCommand(const char (&rxbuffer)[3])
{
// Code parses rxbuffer and creates the 3 ints needed
return {repeats, onTicks, offTicks};
}
And then
auto pattern2 = ParseBlinkOnCommand(rxbuffer);
if ( OSTaskCreate( BlinkLED,
pattern2.data(),
&BlinkTaskStack[USER_TASK_STK_SIZE],
BlinkTaskStack,
MAIN_PRIO - 1 ) != OS_NO_ERR )
{
iprintf( "*** Error creating remote blink task\r\n" );
}
I have a struct called SFrame which contains many elements, notably 2 elements which are of type unsigned char*. I create a member variable of this struct in my class but I newly initialize it on each iteration in a function in my class (except for when a certain boolean is true). I do this in the following manner:
if (false == m_bRemainderNeedsProcessing)
{
// ... calls before and after the initialization are unimportant and not shown
m_sFrame = SFrame();
}
Then I pass m_sFrame to a function to assign some of its elements and then I need to assign an unsigned char array to my pszMessage variable in my struct.
m_sFrame.iMessageSize = m_sFrame.iPayloadLength;
m_sFrame.iOriginalMessageSize = m_sFrame.iPayloadLength;
m_sFrame.pszMessage = new unsigned char[m_sFrame.iPayloadLength + Constants::RSSL_DECODE_PADDING];
m_sFrame.pszOriginalMessage = new unsigned char[m_sFrame.iPayloadLength + Constants::RSSL_DECODE_PADDING];
These SFrame instances are stored in a vector of SFrames i.e.
std::vector<SFrame>;
I want to be able to reuse m_sFrame for each iteration but I have to ensure that if I am going to clear the contents of the SFrame, that when I store it inside the vector, the SFrame is copied into the vector without losing it's assigned values. For this I create a copy constructor for SFrame:
I have attached an image of part of the SFrame's copy constructor.
At the end of my function, I clear the memory in the pszMessage (and the pszOriginalMessage which is almost the same) by doing the following:
ClearMemory(m_sFrame.pszMessage);
Where the ClearMemory function does the following:
void CPCAPParser::ClearMemory(unsigned char *pszBuffer)
{
if(pszBuffer != NULL)
{
delete [] pszBuffer;
}
}
Thee problem is, this function seems to be deleting more than it should do.... because after many iterations, I get an unhandled exception: Access Violation...
I've attached a few images that might help convey what the problem is. Really need help with this :(, if anyone needs me to add more details let me know.
Thanks
http://imageshack.com/f/pduGDLGZp (Constants::RSSL_DECODE_PADDING has length 7 so there are 13 bytes in total which have been set - made evident at the start of the memory block).
http://imageshack.com/f/exRaaEmip - Where I am calling ClearMemory (the memory address is obviously still the same).
I would post more images but I don't have enough rep...
SFrame:
struct SFrame
{
int* ipTemp_int_ptr;
int* ipTemp_int_ptr_actual;
int* piTimestampPos;
int* piOffset;
int iIP_Header_Length;
int iTCP_Header_Length;
int iTCP_Source_Port;
int iTCP_Dest_Port;
long long uiSequenceNumber;
long long uiInitialSequenceNumber;
long long uiAckNumber;
int iIp_total_length;
int iActual_frame_length;
int iOriginal_frame_length;
int iCaptured_frame_length;
int iTotalPayloadLength;
int iTotalMsgLoad;
int iPayloadLength;
int iBytesComplete;
int iFragmentID;
int iRemainder;
int iMessageSize;
int iOriginalMessageSize;
long long iNextExpectedSequenceNum;
std::string strSourceAddress;
std::string strDestAddress;
std::string strTimestamp;
unsigned char* pszMessage;
unsigned char* pszOriginalMessage;
unsigned int uiClientID;
int iStartOfRemainder;
int iAccumulatedMsgLength;
SFrame() : ipTemp_int_ptr ( NULL ),
ipTemp_int_ptr_actual ( NULL ),
piTimestampPos ( NULL ),
piOffset ( NULL ),
pszMessage ( NULL ),
pszOriginalMessage ( NULL ),
iIP_Header_Length( 0 ),
iTCP_Header_Length ( 0 ),
iTCP_Source_Port ( 0 ),
iTCP_Dest_Port ( 0 ),
iIp_total_length ( 0 ),
iActual_frame_length ( 0 ),
iOriginal_frame_length ( 0 ),
iCaptured_frame_length ( 0 ),
uiSequenceNumber( 0 ),
uiInitialSequenceNumber ( 0 ),
uiAckNumber( 0 ),
iPayloadLength ( 0 ),
iNextExpectedSequenceNum ( 0 ),
uiClientID ( 0 ),
iMessageSize ( 0 ),
iOriginalMessageSize ( 0 ),
iFragmentID( 0 ),
iTotalPayloadLength( 0 ),
iBytesComplete( 0 ),
iAccumulatedMsgLength ( 0 ),
iRemainder ( 0 ),
iStartOfRemainder( 0 ),
iTotalMsgLoad ( 0 )
{
}
SFrame(const SFrame &c_rSrc)
{
*this = c_rSrc;
}
SFrame &SFrame::operator=(const SFrame &c_rSrc)
{
iIP_Header_Length = c_rSrc.iIP_Header_Length;
iTCP_Header_Length = c_rSrc.iTCP_Header_Length;
iTCP_Source_Port = c_rSrc.iTCP_Source_Port;
iTCP_Dest_Port = c_rSrc.iTCP_Dest_Port;
iIp_total_length = c_rSrc.iIp_total_length;
iActual_frame_length = c_rSrc.iActual_frame_length;
iOriginal_frame_length = c_rSrc.iOriginal_frame_length;
iCaptured_frame_length = c_rSrc.iCaptured_frame_length;
iPayloadLength = c_rSrc.iPayloadLength;
uiSequenceNumber = c_rSrc.uiSequenceNumber;
uiInitialSequenceNumber = c_rSrc.uiInitialSequenceNumber;
uiAckNumber = c_rSrc.uiAckNumber;
iNextExpectedSequenceNum = c_rSrc.iNextExpectedSequenceNum;
uiClientID = c_rSrc.uiClientID;
iFragmentID = c_rSrc.iFragmentID;
iMessageSize = c_rSrc.iMessageSize;
iOriginalMessageSize = c_rSrc.iOriginalMessageSize;
iTotalPayloadLength = c_rSrc.iTotalPayloadLength;
iBytesComplete = c_rSrc.iBytesComplete;
iAccumulatedMsgLength = c_rSrc.iAccumulatedMsgLength;
iRemainder = c_rSrc.iRemainder;
iStartOfRemainder = c_rSrc.iStartOfRemainder;
iTotalMsgLoad = c_rSrc.iTotalMsgLoad;
strSourceAddress = c_rSrc.strSourceAddress;
strDestAddress = c_rSrc.strDestAddress;
strTimestamp = c_rSrc.strTimestamp;
pszMessage = (c_rSrc.pszMessage == NULL) ? NULL : new unsigned char[c_rSrc.iMessageSize];
pszOriginalMessage = (c_rSrc.pszOriginalMessage == NULL) ? NULL : new unsigned char[c_rSrc.iOriginalMessageSize];
if(pszMessage != NULL)
{
memcpy(pszMessage, c_rSrc.pszMessage, c_rSrc.iMessageSize);
}
if(pszOriginalMessage != NULL)
{
memcpy(pszOriginalMessage, c_rSrc.pszOriginalMessage, c_rSrc.iOriginalMessageSize);
}
return *this;
}
~SFrame()
{
delete [] pszMessage;
delete [] pszOriginalMessage;
}
};
Your problem is that your SFrame struct is not safely asignable, yet you are placing instances of this in a std::vector that will make copies.
Either:
Add a working user-defined copy constructor and assignment operator to your SFrame struct or
Replace the pointer members with std::vector.
You have many members in your struct now. If you miss just one in your copy constructor, or your handling of allocated memory is faulty, you will have a broken copy. Since a vector<SFrame> will create copies, having broken copies is a no-go with vector<SFrame>.
So instead of this, here is the fix using option 2:
#include <vector>
struct SFrame
{
std::vector<int> ipTemp_int_ptr;
std::vector<int> ipTemp_int_ptr_actual;
std::vector<int> piTimestampPos;
std::vector<int> piOffset;
int iIP_Header_Length;
int iTCP_Header_Length;
int iTCP_Source_Port;
int iTCP_Dest_Port;
long long uiSequenceNumber;
long long uiInitialSequenceNumber;
long long uiAckNumber;
int iIp_total_length;
int iActual_frame_length;
int iOriginal_frame_length;
int iCaptured_frame_length;
int iTotalPayloadLength;
int iTotalMsgLoad;
int iPayloadLength;
int iBytesComplete;
int iFragmentID;
int iRemainder;
int iMessageSize;
int iOriginalMessageSize;
long long iNextExpectedSequenceNum;
std::string strSourceAddress;
std::string strDestAddress;
std::string strTimestamp;
std::vector<unsigned char> pszMessage;
std::vector<unsigned char> pszOriginalMessage;
unsigned int uiClientID;
int iStartOfRemainder;
int iAccumulatedMsgLength;
SFrame() :
iIP_Header_Length( 0 ),
iTCP_Header_Length ( 0 ),
iTCP_Source_Port ( 0 ),
iTCP_Dest_Port ( 0 ),
iIp_total_length ( 0 ),
iActual_frame_length ( 0 ),
iOriginal_frame_length ( 0 ),
iCaptured_frame_length ( 0 ),
uiSequenceNumber( 0 ),
uiInitialSequenceNumber ( 0 ),
uiAckNumber( 0 ),
iPayloadLength ( 0 ),
iNextExpectedSequenceNum ( 0 ),
uiClientID ( 0 ),
iMessageSize ( 0 ),
iOriginalMessageSize ( 0 ),
iFragmentID( 0 ),
iTotalPayloadLength( 0 ),
iBytesComplete( 0 ),
iAccumulatedMsgLength ( 0 ),
iRemainder ( 0 ),
iStartOfRemainder( 0 ),
iTotalMsgLoad ( 0 )
{
}
};
Note that the copy constructor and assignment operator (and destructor) are now gone, thus making the code a lot easier to handle, as well as not having a chance of missing any of the members during the copy. Instead we let the compiler generate the copy, and the compiler will always get every member copied.
Now, your code base that uses the struct has to be recompiled, and you will inevitably get compiler errors. However those errors are usually very easy to fix. Most will probably require you to
Remove the lines with delete [] somepointer; where somePointer is now a vector and
If passing a pointer to the beginning of the buffer, you pass &vector[0] or vector.data(), since a vector is basically a wrapper for new[]/delete[].
Going back to your original code, one issue with your assignment operator is that you failed to delete the previous memory allocated, thus you have a memory leak. Also, you didn't check for self-assignment, given the way you wrote the copy operations. However, this may not have been the only error, as we didn't see how you're using these SFrame instances.
Therefore it may be better to change to vector, fix the compiler errors, rebuild and test your app.
class SuperClass{
/* ==================== METHODS ======================================= */
void
setValue (
std::string name,
int i ) {
MemberMapIterator it = memberMap_.find ( name );
if ( it != memberMap_.end ( ) ) {
void* ptr = ( *it ).second;
long long classPtr = reinterpret_cast< long long > ( this );
long long memberPtr = reinterpret_cast< long long > ( ptr );
int* value = reinterpret_cast< int* > ( classPtr + memberPtr );
( *value ) = i;
}
} // setValue
int
getValue (
std::string name ) {
MemberMapIterator it = memberMap_.find ( name );
if ( it != memberMap_.end ( ) ) {
void* ptr = ( *it ).second;
long long classPtr = reinterpret_cast< long long > ( this );
long long memberPtr = reinterpret_cast< long long > ( ptr );
int* value = reinterpret_cast< int* > ( classPtr + memberPtr );
return *value;
}
return -234234;
} // getValue
protected:
/* ==================== METHODS ======================================= */
void
Build ( ) {
configure ( );
} // Build
void
AddMember (
std::string name,
void* ptr ) {
memberMap_.insert ( MemberMapPair ( name, ptr ) );
} // AddMember
/* ==================== STATIC METHODS======================================= */
virtual void
configure ( ) = 0;
private:
/* ==================== METHODS ======================================= */
/* ==================== DATA MEMBERS ======================================= */
MemberMap memberMap_;
};
class SubClass: public SuperClass {
public:
/* ==================== LIFECYCLE ======================================= */
SubClass( ) : age_ ( 0 ) {
Build ( );
} /* constructor */
~SubClass( ) /* destructor */
{ }
protected:
/* ==================== STATIC METHODS======================================= */
void
configure ( ) {
long long classPtr = reinterpret_cast< long long > ( this );
long long agePtr = reinterpret_cast< long long > ( &this->age_ );
void* ptr = reinterpret_cast< void* > ( agePtr - classPtr );
this->AddMember ( "age", ptr );
} // configure
private:
/* ==================== DATA MEMBERS ======================================= */
int age_;
}
In SubClass, I add the offset of the private class field (thinking about the class as a C structure) the Super class map using string name as a key.I will make to execute configure only once and then I want to use this offset for every Person instance to access to its private fields at runtime (this + offset = field). Will be this safe? I tested this code and its work it is doing what I want. But should I expect any memory violations or something else (assuming that it won't be intentional violation (programmer errors))?
First of all, it's worth knowing that C++ classes are not like C structs. The compiler puts extra things in classes like the vtable pointer, which could be in the beginning, the end, or somewhere else depending on the compiler. There is one kind of class that behaves like a C struct (i.e. a bag of bits) and they're called Plain Old Data types (POD). You can find plenty on them on StackOverflow. Since you're using inheritance, you're not using a POD.
If you're trying to forcefully access private members, you probably need to rework your design. You should ask yourself why they're private in the first place. Based on what it looks like you're going for in the code, I can think of more straightforward approaches:
Cast the base class to the subclass, then set the private member with a setter function.
You could make setValue and getValue virtual and override it in the subclass.
Use friendship.